React Child Component Not Updating After Parent State Change
Solution 1:
There are two issues with your code.
Your child component's initial state is set from props.
this.state = {
data: props.data
};
Quoting from this SO Answer:
Passing the intial state to a component as a
prop
is an anti-pattern because thegetInitialState
(in our case the constuctor) method is only called the first time the component renders. Never more. Meaning that, if you re-render that component passing a different value as aprop
, the component will not react accordingly, because the component will keep the state from the first time it was rendered. It's very error prone.
So if you can't avoid such a situation the ideal solution is to use the method componentWillReceiveProps
to listen for new props.
Adding the below code to your child component will solve your problem with Child component re-rendering.
componentWillReceiveProps(nextProps) {
this.setState({ data: nextProps.data });
}
The second issue is with the fetch
.
_makeApiCall(endpoint) {
fetch(endpoint)
.then((response) => response.json()) // ----> you missed this part
.then((response) => this.setState({ response }));
}
And here is a working fiddle: https://jsfiddle.net/o8b04mLy/
Solution 2:
If the above solution has still not solved your problem I'll suggest you see once how you're changing the state, if you're not returning a new object then sometimes react sees no difference in the new previous and the changed state, it's a good practice to always pass a new object when changing the state, seeing the new object react will definitely re-render all the components needing that have access to that changed state.
For example: -
Here I'll change one property of an array of objects in my state, look at how I spread all the data in a new object. Also, the code below might look a bit alien to you, it's a redux reducer function BUT don't worry it's just a method to change the state.
export const addItemToCart = (cartItems,cartItemToBeAdded) => {
return cartItems.map(item => {
if(item.id===existingItem.id){
++item.quantity;
}
// I can simply return item but instead I spread the item and return a new object
return {...item}
})
}
Just make sure you're changing the state with a new object, even if you make a minor change in the state just spread it in a new object and then return, this will trigger rendering in all the appropriate places. Hope this helped. Let me know if I'm wrong somewhere :)
Solution 3:
There are some things you need to change.
When fetch
get the response, it is not a json.
I was looking for how can I get this json and I discovered this link.
By the other side, you need to think that constructor
function is called only once.
So, you need to change the way that you retrieve the data in <Child>
component.
Here, I left an example code: https://jsfiddle.net/emq1ztqj/
I hope that helps.