Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React: componentDidMount + setState not re-rendering the component

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 (
                ...
        );
    }
 }
like image 822
laos Avatar asked Nov 18 '16 18:11

laos


People also ask

Is it OK to setState in componentDidMount?

According to the React Documentation it's perfectly OK to call setState() from within the componentDidMount() function. It will cause render() to be called twice, which is less efficient than only calling it once, but other than that it's perfectly fine.

Does componentDidMount run every render?

componentDidMount() only runs once after the first render. componentDidMount() may be called multiple times if the key prop value for the component changes. componentDidMount method is used for handling all network requests and setting up subscriptions during the mounting phase.

Why is componentDidMount not working?

Although you have a console log statement in your componentDidMount function it may not be executing because that function never gets run. If the error occurs before componentDidMount gets called such as in your render function, you won't see the console log.

Why is React not updating on state change?

State updates in React are asynchronous; when an update is requested, there is no guarantee that the updates will be made immediately. The updater functions enqueue changes to the component state, but React may delay the changes, updating several components in a single pass.


Video Answer


1 Answers

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>
  )
}
like image 157
azium Avatar answered Oct 15 '22 09:10

azium