componentDidUpdate called twice every route change - reactjs

In my application i have a HomePage component which has navlinks in it. the route for this component is <Route to="/browse/:item" exact component={HomePage} />. so the component has a navigation bar with NavLink links to "sub routes" of it. for example a NavLink that leads you to /browse/featured or
/browse/new and i added to this component the lifecycle method componentDidUpdate() to just console.log("UPDATED HOMEPAGE") and whenever i click a NavLink this happens:
i tried to use shouldComponentUpdate with the nextProps and nextState params to see if indeed the state or props are changed (something that will cause a re-render) but they stay the same.
Thanks in advance for your help.
EDIT:
the code is on github https://github.com/idanlo/spotify-test-frontend
components that have the problem that i have seen are AlbumView and HomePage
ANOTHER EDIT:
this is a console.log() of the two updates happening, each one displaying the props before and after the update. on the first update you can see that the url is different so the update is supposed to happen but on the second update nothing is different, everything is the same (in the state everything is the same too)

Usually there are multiple calls because of changes made to the state. Check other files to make sure that no actions that modify the state are initially called. Even though you check for differences for nextProps, the variable that changes might not be in props.

I suspect that Navlink's internal implementation uses setState with an updater function which is the source of this duplicate log
I found that when I update state with an updater function then componentDidUpdate gets fired twice!
However when I pass an object to setState - componentDidUpdate is fired only once.
setState(updater[, callback])
example:
incrementScore = () => {
this.setState(prevState => ({
score: prevState.score + 1
}));
};
VS
setState(stateChange[, callback])
example:
this.setState({quantity: 2})
I guess it's because
Think of setState() as a request rather than an immediate command to update the component. For better perceived performance, React may delay it, and then update several components in a single pass. React does not guarantee that the state changes are applied immediately.
read about setState on React Docs

Related

What renders a function in React

I have following component:
const RenderJobsTable = props => {some render stuff in here};
And I call that function in another functions render:
<Collapse isOpen={toggleJobTable} id="collapseExample">
<CardBody>
<RenderJobsTable
crawl={props.crawl}
jobs={jobs}
></RenderJobsTable>
</CardBody>
</Collapse>
I noticed that every time I collapse the outer element and toggle it so it gets visible again, the function is called again. What exactly triggers the function to be called in React? Is it when the element is visible? Is if if one of it's parameters changes? Or if parent functions gets changed? If I just toggle the element and I have some call's to a rest service I don't want that to happen all the time. So I need to understand what triggers the call.
Basically, Reacts render method gets triggered almost every time something related to the state of the component changes. From How does React decide when to re render a component?:
A re-render can only be triggered if a component’s state has changed. The state can change from a props change, or from a direct setState change. The component gets the updated state and React decides if it should re-render the component. Unfortunately, by default React is incredibly simplistic and basically re-renders everything all the time.
Component changed? Re-render. Parent changed? Re-render. Section of props that doesn't actually impact the view changed? Re-render.
You can also refer to a similar question's answer.
Related to when to make API calls, I strongly suggest that you read about React component Lifecycle (for example here or here).
TLDR, place your API calls in componentDidMount().

React, componentWillReceive lifeCycle Hook not being invoked (on passing props)

I was trying to pass props both in App component (The root Component) and then to Header component from App itself.
I have used Life Cycle Hooks upto componentWillReceiveProps() in both App and Header Component.
componentWillMount(), render(), componentDidMount() are getting executed in both the Components in an expected order.
However, componentWillReceiveProps() is not executed at all even on passing props. This is a unExpected behaviour, since componentWillMount() was executed normally!
I shall be extremely thankful to know why is this happening ! Thank you :)
Kindly check the code sample from the below link:
https://codesandbox.io/s/r092xkpwjp
Please Note: Question has been updated, and it shows both scenarios now, new props being passed (in Header Component which works fine now) and no new props being passed as was previously the case in the question (in App Component which still shows why things were working unexpectedly).
I don't see why you expect your components to be updated as the The props being passed to them always stay the same and no new props have been passed to these at all, but generally you should use componentDidUpdate(prevProps, prevState).
componentWillReceiveProps() only gets invoked when the props passed to them are new props, different from the previous values. In the question this was not happening.
Note: The question has been updated now for it to work properly.
Also consider managing the state by static getDerivedStateFromProps(props, state), i.e.:
static getDerivedStateFromProps(props, state){
// just return the state
return {
isLoading: false,
money: props.money
}
}
- it's executed on init + on updates.
As stated in the official documentation (see https://reactjs.org/docs/react-component.html) the
componentWillReceiveProps()
lifecycle method is deprecated and should be avoided.
The reason why it is never called is that, to help user avoid it, React developers renamed it
UNSAFE_componentWillReceiveProps()
You should however avoid it, since they plan to deprecate that method
1.The main reason why the componentWillReceiveProps() was not being invoked was because my props passed to Header Component were not changing at all, I have been passing the same props again and again. And componentWillReceiveProps() gets executed only when the props being passed are different each time.
This is what my Header component looked at the time of asking this question.
<Header
menus={["home", "about", "services", "blog"]}
/>
The Header Component had only the menus prop as an array (at the time of asking this question) and no event was updating this menus prop , that's the reason why my
componentWillReceiveProps() was never invoked/called in the Header Component.
Note: To simply the things I now passed another prop to Header Component, and started to test my code on this prop instead of working with the array menus prop.
<Header
menus={["home", "about", "services", "blog"]}
prop={this.state.prop}
/>
And I am making my state.prop to update using an event handler:
// Dynamically sending Props
handlePropSending = () => {
this.setState({
...this.state,
prop: this.state.prop + 1
});
};
And as I am clicking on the button 'Sending Props to Header', the new props are sent to Header Component and our componentWillReceiveProps is being called and executed succefully.
And same was the issue with componentWillReceiveProps in the App Component
ReactDOM.render(, rootElement);
Since I was adding money prop (money={9199}), there actually is no Parent Component available for money prop to update it's value (which we could have passed to the App Component for it to invoke its componentWillReceiveProps method).
NOTE: The code is updated now and there are event handlers now to make sure the props keep updated and now the componentWillReceiveProps() method is indeed being successfully invoked.
componentWillReceiveProps demonstrated along with other life Cycle Hooks
LIKE THEY SAY :) ALL IS WELL THAT ENDS WELL :)

Will componentWillMount run again if component is re-rendered because of parent component?

Will componentWillMount run again if component is re-rendered because of parent component?
No, componentWillMount is called only once.
Also, componentDidMount is called only once.
componentDidUpdate is called on every re-render.
To correctly understand about the react lifecycle methods you can go through this link.
https://engineering.musefind.com/react-lifecycle-methods-how-and-when-to-use-them-2111a1b692b1
The short answer is NO
It's called once right before your component is about to be rendered to the DOM.
The long answer is:
componentWillMount
Your component is going to appear on the screen very shortly. That chunky render function, with all its beautifully off-putting JSX, is about to be called.
Most Common Use Case: App configuration in your root component.
Can call setState: Yes, but don't. Use the default state instead.
componentDidMount
Here is where you load in your data. ComponentDidMount is also where you can do all the fun things you couldn’t do when there was no component to play with. Basically, here you want to do all the setup you couldn’t do without a DOM, and start getting all the data you need. Most Common Use Case: Starting AJAX calls to load in data for your component.
componentWillReceiveProps
Perhaps some data that was loaded in by a parent component’s componentDidMount finally arrived and is being passed down. Before our component does anything with the new props, componentWillReceiveProps is called, with the next props as the argument.
shouldComponentUpdate
shouldComponentUpdate should always return a boolean — an answer to the question, “should I re-render?” Yes, little component, you should. The default is that it always returns true. It's an awesome place to improve performance.
componentWillUpdate
Most Common Use Case: Used instead of componentWillReceiveProps on a component that also has shouldComponentUpdate (but no access to previous props). It’s basically the same as componentWillReceiveProps, except you are not allowed to call this.setState.
componentDidUpdate
Here we can do the same stuff we did in componentDidMount — reset our masonry layout, redraw our canvas, etc. Basically, we use it when it's all said and done, but we don’t want to waste time to redraw the canvas every time it updates. Most Common Use Case: Updating the DOM in response to prop or state changes.
componentWillUnmount
Here you can cancel any outgoing network requests, or remove all event listeners associated with the component. Basically, clean up anything to do that solely involves the component in question — when it’s gone, it should be completely gone.

Weird component lifecycle with react router

I have this route (simplified)
<Route
path="/foo/:id">
render={({match}) => (
<Page key={match.params.id} id={match.params.id}/>
)}
</Route>
Now the reason for that is that when I go from /foo/1 to /foo/2 I don't want to clear and update state of an existing component, it's too complex in my case. So I have a dynamic key which causes to unmount the /foo/1 component and mount a brand new /foo/2 component.
So far so good. But here is the catch. The lifecycle I observed is this
/foo/1 re-render
/foo/2 constructor
/foo/2 componentDidMount
/foo/1 componentWillUnmount
/foo/2 render
Why doesn't the first component unmount before the second one mounts? Because of that I get weird re-renders and glitches (it's connected to redux store and initialization of the /foo/2 component changes the store which manages to re-render still existing component /foo/1). Any ideas how to avoid this?
Thanks
It is hard to determine exactly what is happening with your short snippet of code, but, I believe your issues are relating to some anti-patterns;
A react component constructor should only be used for two things:
Initialising local state
Binding event handlers
https://reactjs.org/docs/react-component.html#constructor
Secondly you are using the render prop with a inline function, this means when ever a matching route is detected and the parent component re-renders it will re-construct the component, un-mounting it first then mounting it again. To prevent this you should be defining the render function outside of the parents render function.
Thirdly because you have a dynamic route /foo/:id and assigning the value of :id to the key of the child component react sees it as a different component and doesn't unmount it until the current render process has reached the un-mounting stage. This means that for a short period of time you have <Page key={1} /> & <Page key={2}/> present.
Because these three issues, you are getting what seems to be a weird component lifecycle but in fact it is react doing exactly what you told it to do.
The Solution:
Move the redux action being invoked in the constructor of the <Page /> to be in the componentDidMount() function.
Move the inline render function to be outside the parent render function.
Remove the key prop.

Does my component need force update to scrollIntoView?

I'm making my first react + redux app and I have a question. I have a component that render a input field and, as some content is added above it, I need it to scrollIntoVIew.
So, I just added the scrollIntoView in the component componentDidUpdate() lifecycle method :
componentDidUpdate() {
console.log("scroll");
this.refs.input.scrollIntoView();
}
The only problem is that this component does not re render each time (as nothing change in it) so the scrollIntoVIew is called only one time.
I could force update to component but this is not really good practice isn't it ? What can I do in this case ?
You could call setState and update some object in the component's state every time content is added to re-render the component, and componentDidUpdate will be invoked.
You could also use the second argument (callback) to setState, but the React docs say the following about second argument of setState:
The second parameter is an optional callback function that will be
executed once setState is completed and the component is re-rendered.
Generally we recommend using componentDidUpdate() for such logic
instead.

Resources