How to update child component after props are changed in parent component - reactjs

I have component TopicsView where I have property called choosenTopicId which I change on button click. Inside this component I have child component
<TopicViewOnSide TopicId={this.state.choosenTopicId} />
Inside TopicViewOnSide I use TopicId in componentDidMount() when I'm sending request from axios (HTTP GET) to get topic data from the server and then component TopicViewOnSide renders. My problem is when I change choosenTopicId by clicking button in TopicView, props are changed and it's good. But the child component doesn't rerender when the props are changed and I have old topic data because the child component didn't call componentDidMount() and there was no request to server. Can I somehow call the method componentDidMount() when the props are changed so the request from axios will be send and my data will be updated ?

You're looking for componentDidUpdate() which gets triggered after every state or prop change. The componentDidMount() method only runs a single-time.
You can do something like:
componentDidUpdate(prevProps){
if(this.props.blaw !== prevProps.blaw){
...execute your logic
}
}
That creates a check to do something only when props change.

Related

Use HOC, React Context and componentDidMount API to get uid form Firebase

I try to use a HigherOrderComponent (HOC) to provide the uid of a Firebase user to a component (Comp). The value is succesfully retrieved but I don't succeed in getting it in the wrapped component (Comp).
Below I describe the order of events in the HOC and wrapped Component (Comp):
HOC:
constructor() sets state.uid = null;
render() wraps the Comp in a Provider component with value=this.state.uid.
The Consumer component with prop uid={value} (where value is provided by the Provider component) wraps the Comp
In the componentDidMount() I make an asynchronous call to the Firebase backend (firebase.auth.onAuthStateChanged()).
Comp:
render() fires;
componentDidMount() fires. this.props.uid is null because the asynchronous call didn't finish yet;
HOC:
in componentDidMount() the call to Firebase backend resolves. If not null I do this.setState({uid: value}) where value is the value from asynchronous call. That triggers a new render() of the HOC. A Provider component with the new value this.state.uid is returned.
Comp:
render() runs for 2nd time;
componentDidMount() should run so I can access the this.props.uid and use that value to retrieve data and other stuff but... this.props.uid is null.
I thought componentDidUpdate is supposed to run after a Component's render methode fires but here this doesn't happen. Setting breakpoints or console.log() shows this.
Funny thing is that when in the Comp render() method I display this.props.uid object (via JSON.stringify()) it shows! So the render method can access the updated props value but componentDidUpdate never...
What's wrong here? Am I missing something re: Reacts lifecycle methods?

React Navigation: How to update child state when parent state gets updated?

The data of parent is passed to child via:
this.props.navigation.navigate("foo", {data: data});
Whenever one navigates to any screen, the current state of objects is sent as props to the navigated component.
Having said that, the navigate function should be associated with a function/component placed in render(). So, if data is updated, render() gets called again and you're navigated to the new screen with updated data.

`componentWillReceiveProps` explanation

I recently wanted to upgrade my knowledge of React, so I started from the component lifecycle methods. The first thing that got me curious, is this componentWillReceiveProps. So, the docs are saying that it's fired when component is receiving new (not necessarily updated) props. Inside that method we can compare them and save into the state if needed.
My question is: Why do we need that method, if changes in props of that component (inside parent render) will trigger the re-render of this child component?
One common use case are state (this.state) updates that may be necessary in response to the updated props.
Since you should not try to update the component's state via this.setState() in the render function, this needs to happen in componentWillReceiveProps.
Additionally, if some prop is used as a parameter to some fetch function you should watch this prop in componentWillReceiveProps to re-fetch data using the new parameter.
Usually componentDidMount is used as a place where you trigger a method to fetch some data. But if your container, for example, UserData is not unmounted and you change userId prop, the container needs to fetch data of a user for corresponding userId.
class UserData extends Component {
componentDidMount() {
this.props.getUser(this.props.userId);
}
componentWillReceiveProps(nextProps) {
if (this.props.userId !== nextProps.userid) {
this.props.getUser(nextProps.userId);
}
}
render() {
if (this.props.loading) {
return <div>Loading...</div>
}
return <div>{this.user.firstName}</div>
}
}
It is not a full working example. Let's imagine that getUser dispatch Redux action and Redux assign to the component user, loading and getUser props.
It 'serves' as an opportunity to react to the incoming props to set the state of your application before render. If your call setState after render you will re-render infinitely and that's why you're not allowed to do that, so you can use componentWillReceiveProps instead.
But... you are beyond CORRECT in your confusion, so correct in fact that they are deprecating it and other Will-lifecycle hooks Discussion Deprecation.
There are other ways to accomplish what you want to do without most of those Will-lifecycle methods, one way being don't call setState after render, just use the new incoming props directly in render (or wherever) to create the stateful value you need and then just use the new value directly, you can then later set state to keep a reference for the next iteration ex: this.state.someState = someValue, this will accomplish everything and not re-render the component in an infinite loop.
Use this as an opportunity to react to a prop transition before render() is called by updating the state using this.setState(). The old props can be accessed via this.props. Calling this.setState() within this function will not trigger an additional render.
Look at this article
the componentWillReceiveProps will always receive as param "NxtProps", componentWillReceiveProps is called after render().
some people use this method use this to compare nxtProps and this.props to check, if something should happen before the component call render, and to some validations.
check the react's documentation to know more about react lifecycle!
hope this could help you!
changes in props of that component (inside parent render) will trigger the re-render of this child component
You are absolutely right. You only need to use this method if you need to react to those changes. For instance, you might have a piece of state in a child component that is calculated using multiple props.
Small Example:
class Test extends Component {
state = {
modified: "blank"
};
componentDidMount(){
this.setState({
modified: `${this.props.myProp} isModified`
});
}
componentWillReceiveProps(nextProps) {
this.setState({
modified: `${nextProps.myProp} isModified`
});
}
render() {
return <div className="displayed">{this.state.modified}</div>
}
}
In this example, componentDidMount sets the state using this.props. When this component receives new props, without componentWillReceiveProps, this.state.modified would never be updated again.
Of course, you could just do {this.props.myProp + "IsModified"} in the render method, but componentWillReceiveProps is useful when you need to update this.state on prop changes.

React Router 4 - componentWillReceiveProps() doesn't fire

I'm using React Router 4.
When I render component with render parameter componentWillReceiveProps() it doesn't fire the fist time, so I need to click twice to sent props to the component.
I render like this:
const CartRoute = (props) => (<Cart itemsInCart = {this.state.itemsInCart} deleteItemFromCart = {this.deleteItemFromCart} {...props} />);
.....
<Switch>
.....
<Route path="/cart" render={CartRoute} />
</Switch>
Without Router (when all components are on the same page) it works ok.
Here is detailed description:
React router - Need to click LINK twice to pass props to Component
I think Reason is simple one, As per DOC:
React doesn't call componentWillReceiveProps with initial props during
mounting. It only calls this method if some of component's props may
update. componentWillReceiveProps() is invoked before a mounted component receives new props.
componentWillReceiveProps will not get called when first time component get rendered, at that time componentDidMount get called, when you do any changes in props values then only componentWillReceiveProps will get triggered. So first time if you want to do some calculation do that in componentDidMount method, like this:
componentDidMount(){
console.log('props values', this.props); //it will print the props values
}
componentWillReceiveProps is a Updating lifecycle method not Mounting method.
Mounting Methods:
These methods are called when an instance of a component is being
created and inserted into the DOM.
Updating Methods:
An update can be caused by changes to props or state. These methods
are called when a component is being re-rendered.
Check the difference between Mounting and Updating lifecycle method.

ReactJS Is it adviceable to update props

Is it adviceable to change the props to load my page with fresh data.
I have a requirement where I need to load the HTML page from JSON, example given below
formSchema:{
"label": "Step 1",
"action": "Next",
"Fields":[
{
"type":"text",
"label":"First Name",
"name":"fname"
"value":"Abraham"
}
]
}
I'm able to load the HTML page from the above JSON schema, but when user clicks the submit button there will a new schema loaded through ajax call. Now if I pass the new schema to my reducers can I directly replace the props with the newSchema, as I heard mutating the props is not adviceable. In such case how do I update my props only with new data.
If I do it as follows in my reducer
return Object.assign(...formSchema, action.formSchema);
I see in console the next state is shown as object instead of an array even though I load the same set of data.
You can use componentWillReceiveProps() method in React to update your view with new props.
componentWillReceiveProps() is invoked before a mounted component receives new
props. If you need to update the state in response to prop changes (for example, to reset it), you may compare this.props and nextProps and perform state transitions using this.setState() in this method.
Note that React may call this method even if the props have not changed, so make sure to compare the current and next values if you only want to handle changes. This may occur when the parent component causes your component to re-render.
React doesn't call componentWillReceiveProps with initial props during mounting. It only calls this method if some of component's props may update. Calling this.setState generally doesn't trigger componentWillReceiveProps.
It looks like this:
class YourComponent extends Component{
constructor(props) {
this.state = {
message: props.message // initial prop
}
}
componentWillReceiveProps(nextProps){
this.setState({message: nextProps.message} // updated prop
}
render() {
return (
<div><h1>{this.state.message}</h1></div>
)
}
}
Note: In this code you're not updating/mutating props, instead you're using new props (nextProps) and updating the state of your view.

Resources