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
You are falling victim to this antipattern.
In
MyComponent
constructor, which only gets called the first time it mounts, passed your empty array throughnew 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.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.