React useEffect equivalent in class component

Viewed 19261

I have a React functional component that does the following:

  • Sends a POST to load raw data from my API
  • (Among other stuff) sends mapped data to a custom table component
  • Allows the user to re-send the POST with a different fiter
import React, { useEffect, useState } from "react";
import axios from "axios";
import mytable from "./mytable";

const baseRawData = [
  { name: "name", value: "John Doe" },
  { name: "sex", value: "M" }
];

export default function FuncComp() {
  let [rawData, setRawData] = useState(baseRawData);
  let [mappedData, setMappedData] = useState([]);
  let [apiFilter, setApiFilter] = useState("all");

  useEffect(() => {
    setMappedData(
      rawData.map(e => ({
        ...e
        // Some logic
      }))
    );
  }, [rawData]);

  useEffect(() => {
    axios.post(`/my/api/${filter}`).then(({ data }) => {
      setRawData(data);
    });
  }, [apiFilter]);

  return (
    <div>
      <h1>The filter is '{apiFilter}'</h1>
      <mytable data={mappedData} />
      <input
        value={apiFilter}
        onBlur={e => setApiFilter(e.target.value)}
        type="text"
      />
    </div>
  );
}

How would I, in a class component, achieve the same logic as useEffect to map the newly received raw data every time the user changes the filter and new raw data is received?

2 Answers

Before converting it back to a class component, I would simplify the functional one like this

import React, { useEffect, useState } from "react";
import axios from "axios";
import mytable from "./mytable";

const baseRawData = [
  { name: "name", value: "John Doe" },
  { name: "sex", value: "M" }
];

const mapData = rawData => rawData.map(e => ({
  ...e
  // Some logic
}));

export default function FuncComp() {
  let [mappedData, setMappedData] = useState(mapData(baseRawData));
  let [apiFilter, setApiFilter] = useState("all");

  useEffect(() => {
    axios.post(`/my/api/${apiFilter}`).then(({ data }) => {
      setMappedData(mapData(data));
    });
  }, [apiFilter]);

  return (
    <div>
      <h1>The filter is '{apiFilter}'</h1>
      <mytable data={mappedData} />
      <input
        value={apiFilter}
        onBlur={e => setApiFilter(e.target.value)}
        type="text"
      />
    </div>
  );
}

and then you can convert useState in simple state vars. I didn't run the code but it should be something like this.

import React from "react";
import axios from "axios";
import mytable from "./mytable";

const baseRawData = [
  { name: "name", value: "John Doe" },
  { name: "sex", value: "M" }
];

const mapData = rawData => rawData.map(e => ({
  ...e
  // Some logic
}));

class component extends React.Component {
  constructor() {
    this.state = {
      mappedData: mapData(baseRawData),
      apiFilter: 'all'
    }
  }

  filterChanged(newApiFilter) {
    this.setState({apiFilter: newApiFilter});
    axios.post(`/my/api/${newApiFilter}`).then(({ data }) => {
      this.setState({
        mappedData: mapData(data)
      })
    });
  }

  render () {
    const {apiFilter, mappedData} = this.state;
    return (
      <div>
        <h1>The filter is '{apiFilter}'</h1>
        <mytable data={mappedData} />
        <input
          value={apiFilter}
          onBlur={e => this.filterChanged(e.target.value)}
          type="text"
        />
      </div>
    );
  }
}

This solution is based on the following assumptions;

  1. You have a baseData you want to render when the component first render.
  2. Once the component is mounted, you request new data from the api.
  3. If the filter is empty, you make a request without the filter.
  4. Once the filter value is set, you make another request with the filter.

Code

import React, { Component } from "react";
import axios from "axios";
import mytable from "./mytable";

const baseRawData = [
  { name: "name", value: "John Doe" },
  { name: "sex", value: "M" }
];

export default class ClassComp extends Component{
  state = {
    rawData: baseRawData,
    mappedData: [],
    apiFilter: ""
  }

  componentDidMount() {
    axios.post(`/my/api`)
    .then(({ data }) => {
      this.setState({ mappedData : data });
    });
  }

  filterPost = (e) => {
    this.setState({ apiFilter: e.target.value},() => {
      axios.post(`/my/api/${this.state.apiFilter}`)
        .then(({ data }) => {
          this.setState({ mappedData : data });
        });
    });
  }

  render() {
    const {apiFilter, mappedData, baseRawData } = this.state;

  return (
    <div>
      <h1>The filter is '{apiFilter}'</h1>
      <mytable data={ mappedData ? mappedData : baseRawData } />
      <input
        value={apiFilter}
        onBlur={e => this.filterPost(e.target.value)}
        type="text"
      />
    </div>
  );
 }
}
Related