Weird component lifecycle with react router - reactjs

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.

Related

React rerender props change vs local state change

Why the component Demo does not rerender on counter change?
I understand that props.children are equal to the previous ones, however local state changes, so it should rerender. Is changing local state optimized somehow to detect whether some part of the Top component should be rerendered or shouldn`t?
Children props example
In the second example it does rerender, what is the difference between these examples?
Local state change
In the examples you can click on button and see in the console whether Demo is rerendered.
In the first case you're passing the children which is a part of props and essentially on each re-render of Top component, the same children reference is being returned so React detects that and doesn't re-render the Demo component.
In the second case, React will internally execute React.createElement(...) again to create the Demo component and that's a new reference.
Here is a good line to remember this by :-
if a React component returns the exact same element reference in its
render output as it did the last time, React will skip re-rendering
that particular child
I referred this here - https://blog.isquaredsoftware.com/2020/05/blogged-answers-a-mostly-complete-guide-to-react-rendering-behavior/#component-render-optimization-techniques
This is because passing children argument to the Top component you are passing a reference to this object. React can detect that nothing has changed comparing to the previous version so it will not re-render it.
When you are rendering component using name <Demo /> in your Top component you are rendering a new version of Demo component every time.

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().

Writing optimized React functional components

It is my understanding that the entire React Functional Component gets re-run when re-render is needed or there are any state updates, so how to properly manage the state inside these functions? Is it important to keep it empty of any members like event handlers that you wouldn't want to be re-created every time the function gets re-run?
Is there some sort of best practice for writing optimized functional components?
React Memo is something you might be looking for.
React.memo(...) is a new feature introduced in React v16.6. It works
similiar to React.PureComponent, it helps control functional
components re-renders. React.memo(...) is to functional components
what React.PureComponent is to class components. How do we use
React.memo(…)?
It is very simple. Let’s say we have a functional component, like this:
const Funcomponent = ()=> {
return (
<div>
Hiya!! I am a Funtional component
</div>
)
}
We simply pass the FuncComponent as argument to React.memo function:
const Funcomponent = ()=> {
return (
<div>
Hiya!! I am a Funtional component
</div>
)
}
const MemodFuncComponent = React.memo(FunComponent)
React.memo returned a purified component MemodFuncComponent. This component is what we will render in our JSX markup. Whenever the props and state changes in the component, React will check if the prev state and props and the next props and state are equal if not equal the functional component will re-render if they are equal the functional component will not re-render.
As for second question, yes it's true. You should avoid defining the function inside the render function or in case of functional components, avoid defining inside the function, it will cause unwanted calling of those functions and can result in unwanted behavior as well as cause it to be created every time the component re-renders.
The same goes for adding in-line callback functions for event handlers. They should be defined outside the render function.
As how I get it, You want to know how to manage or improve your react performance right? You are right every Component in react gets re-rendered when there are any state changes in this component or in the parent component (if there is).
So there is a tool (Chrome extension) that helps you finds out which part of the application get re-rendered whenever you make an action React developer tool.

How does react decide to rerender a component

I know React has a life cycle method called shouldComponentUpdate, Which by default return true and that's how the component decides to update
But How does that life cycle method gets called, When a state or props change for that component. What actually happens when we receive new props or state? When We connect a component to redux state and mapStateToProps, Are we checking for a change in values inside the component? If not, When We are looking for a change in state or props?
when the props or state changes, how the life cycle methods are called?. Do we have a listener that calls these methods when the props or state changes?
You should look at lifecycles of both, how they perform and in what order each method gets called. Looking at react lifecycle image bellow you can see the difference between componentWillMount and componentDidMount and others like componentDidUpdate, componentWillUpdate and so on...
Also you should reason when to use each method
To update state you call this.setState() which tells react that something has changed and it will re-render component tree. If you use this.state.data = something react won't trigger render(). Now to update props, you need to understand how render() actually works. This answer is summarized from existing anwser already:
Every time render() is called react will create a new virtual DOM
where the root node is the component whose render function is called.
The render() function is called when either the state or the props of
a component or any of its children change. The render() function
destroys all of the old virtual DOM nodes starting from the root and
creates a brand new virtual DOM.
In order to make sure the re-rendering of components is smooth and
efficient React uses the Diffing Algorithm to reduce the time it takes
to create a new tree to a time complexity of O(n), usually time
complexity for copying trees is > O(n^2). The way it accomplishes this
is by using the "key" attribute on each of the elements in the DOM.
React knows that instead of creating each element from scratch it can
check the "key" attribute on each node in the DOM. This is why you get
a warning if you don't set the "key" attribute of each element, React
uses the keys to vastly increase its rendering speed.
React Lifecycle
Redux Lifecycle
If you use redux library, may be, your component does not re-render after your props changes. Checkout this issue to resolve the props changes problem with componentWillReceiveProps

componentDidUpdate called twice every route change

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

Resources