skip to Main Content

I’m fairly new to react and struggle to update a custom component using componentDidMount and setState, which seems to be the recommended way of doing it. Below an example (includes an axios API call to get the data):

import React from 'react';
import {MyComponent} from 'my_component';
import axios from 'axios';


export default class Example extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            data: []
        };
    }

    GetData() {
        return axios.get('http://localhost:5000/<route>');
    }

    componentDidMount() {
        this.GetData().then(
                (resp) => {
                    this.setState(
                        {data: resp.data}
                    )
                }
            )
    }

    render() {
        return (
            <MyComponent data={this.state.data} />
        );
  } 
}

Doing console.log(this.state.data) just below render() shows that this.state.data does indeed get updated (from [] to whatever the API returns). However, the problem appears to be that MyComponent isn’t rendered afresh by componentDidMount. From the Facebook react docs:

Setting state in this method will trigger a re-rendering.

This does not seem to be the case here: The constructor of MyComponent only gets called once (where this.props.data = []) and the component does not get rendered again. I’d be great if someone could explain why this is and whether there’s a solution or a different way altogether to get the updating done.

UPDATE

I’ve added the code for MyComponent (minus some irrelevant features, as indicated by ...). console.log(data_array) prints an empty array.

import React from 'react';

class DataWrapper {
  constructor(data) {
    this._data = data;
  }

  getSize() {
    return this._data.length;
  }

     ...
}


export class MyComponent extends React.Component {
      constructor(props) {
        super(props);

        this._dataWrapper = new DataWrapper(this.props.data);

        this.state = {
          data_array: this._dataWrapper,
        };

      }

    render() {
        var {data_array} = this.state;
        console.log(data_array);
        return (
                ...
        );
    }
 }

2

Answers


  1. You are falling victim to this antipattern.

    In MyComponent constructor, which only gets called the first time it mounts, passed your empty array through new DataWrapper and now you have some local state which will never be updated no matter what your parent does.

    It’s always better to have one source of truth, just one state object anywhere (especially for things like ajax responses), and pass those around via props. In fact this way, you can even write MyComponent as a simple function, instead of a class.

    class Example extends Component {
      state = { data: [] }
    
      GetData() { .. }
    
      componentDidMount() {
        this.GetData().then(res =>
          this.setState({data: new DataWrapper(res.data)})
        )
      }
    
      render() { return <MyComponent data={this.state.data} /> }
    }
    
    ...
    
    function MyComponent (props) {
      // props.data will update when your parent calls setState
      // you can also call DataWrapper here if you need MyComponent specific wrapper
      return (
        <div>..</div>
      )
    }
    
    Login or Signup to reply.
  2. In other words what azium is saying, is that you need to turn your receiving component into a controlled one. Meaning, it shouldn’t have state at all. Use the props directly.

    Yes, even turn it into a functional component. This helps you maintain in your mind that functional components generally don’t have state (it’s possible to put state in them but … seperation of concerns).

    If you need to edit state from that controlled component, provide the functions through props and define the functions in the "master" component. So the master component simply lends control to the children. They want anything they talk to the parent.

    I’m not posting code here since the ammendment you need to make is negligible. Where you have this.state in the controlled component, change to this.props.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search