I'm fairly new to react and struggle to update a custom ponent using ponentDidMount
and setState
, which seems to be the remended way of doing it. Below an example (includes an axios
API call to get the data):
import React from 'react';
import {MyComponent} from 'my_ponent';
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>');
}
ponentDidMount() {
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 ponentDidMount
. 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 ponent 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 (
...
);
}
}
I'm fairly new to react and struggle to update a custom ponent using ponentDidMount
and setState
, which seems to be the remended way of doing it. Below an example (includes an axios
API call to get the data):
import React from 'react';
import {MyComponent} from 'my_ponent';
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>');
}
ponentDidMount() {
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 ponentDidMount
. 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 ponent 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 (
...
);
}
}
Share
Improve this question
edited Nov 18, 2016 at 18:37
laos
asked Nov 18, 2016 at 18:00
laoslaos
3051 gold badge2 silver badges9 bronze badges
5
-
Can you show the code for
MyComponent
? What you're doing in the container ponent looks right, but something weird might be happening inside the child ponent. – Josh Beam Commented Nov 18, 2016 at 18:04 -
3
The constructor doesn't have to get called more than once. React has already created the object, it doesn't have to do it again. If you log something out in
ponentWillReceiveProps
, you'll see that working fine. And if MyComponent renders out something inthis.props.data
, then that will be updated. – Matis Lepik Commented Nov 18, 2016 at 18:05 - Yea, why does constructor need to be called again? Try putting a console.log in the render to confirm that it renders twice. – Conan Commented Nov 18, 2016 at 18:11
-
Have you checked if the
this
in your "then" arrow function really points to the React class? – Alex. P. Commented Nov 18, 2016 at 18:11 -
I've added the code for
MyComponent
to the original post. – laos Commented Nov 18, 2016 at 18:38
2 Answers
Reset to default 8You 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() { .. }
ponentDidMount() {
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>
)
}
In other words what azium is saying, is that you need to turn your receiving ponent into a controlled one. Meaning, it shouldn't have state at all. Use the props directly.
Yes, even turn it into a functional ponent. This helps you maintain in your mind that functional ponents 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 ponent, provide the functions through props and define the functions in the "master" ponent. So the master ponent 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 ponent, change to this.props.