In the attached example codesandbox I have a Map using react-google-maps.
When wrapping the map with <React.StrictMode> it will stop unmounting components.
I see the way react-google-maps unmounts components is usually by setting their map to null using: instance.setMap(null). I don't understand why this behavior should break under StrictMode.
Here's the codesandbox that demonstrates the problem:
https://codesandbox.io/s/jv61orz64y
Thanks!
Uri
I had the same issue and came across your issue on the repo and therefore this question. I have gone into detail on the issue as a comment on your repo issue, but to summarise for SO:
react-google-maps is a little naughty and actually does some rendering-esque work in the constructor of components. In this case, it initialises the google.maps.Marker and calls setMap. That has the effect of putting the marker on the map.
it removes the markers from the map in componentWillUnmount
a component may be constructed but never ends up being mounted, when this happens, componentWillUnmount is never called, as there's nothing mounted to unmount
this means that if the component is constructed but never mounted, and therefore componentWillUnmount is never called, the marker never gets removed from the map
in StrictMode, this exact situation appears to happen, see explanation below:
If you look at my fork of your demo you'll see that in non-strict mode, the Marker proxy (MyMarker) is constructed, mounted, updated, unmounted and then remounted if you load the page zoom in and then zoom out. However, in strict mode two markers get constructed but only one mounted. So what's happening here is not that the marker is never unmounted as you zoom in, but that there are two markers on the map at all times. One that gets "orphaned" on the map by being constructed but never mounted, and the one that is fully under React's control. It truly is being removed and re-added correctly.
Related
I have been trying to improve rendering performance by using React.memo on components. It works properly until I use it on every single component from the collection being inserted as children.
I prepared snack HERE
To understand what is going on you need to open the console because there are some logs presented.
You will see WrapperMemo which generally renders Child components (they are also wrapped in some auxiliary components to check something I will explain later).
When you press the first button named SLICE MYNUMBERS you will see that React.memo's isEqual function called isWrapperEqual returns true, but it still does not stop from rerendering Child components.
However neither Wrapper nor all other auxiliary wrapper components (ChildOk) are rendered!
When you click any PRESS ME:[number] button it forces you to re-render other Child components when only one is being modified.
It is worth mentioning that TestCompMemo is never re-rendered when any of the buttons are pressed - which is correct behavior.
1) Is there any explanation for that?
2) Is there a way to use React.memo on every component from collection to render only changed ones (as it is presented in Wrapper component) ?
Is there any explanation for that?
In your snack example, you have a Context.Provider wrapper at the top level of the application.
This is what causes the re-render. This is expected behavior and you can read more about it here: https://github.com/facebook/react/issues/15156#issuecomment-474590693
Is there a way to use React.memo on every component from collection to render only changed ones (as it is presented in Wrapper component) ?
Using React.memo on every component is not recommended. You can run into bugs where components do not re-render and you are adding overhead with all the comparisons.
If you want to optimize the application, you might want to run the profiler and figure out which parts of the app is slowing things down and work from there.
But if you want to prevent unnecessary re-renders, you could move context usage up a few levels and pass the onPress to the child components.
Imagine that you've built an application with ReactJs containing a hundred elements. Let's say that in some time, the state of component A changes, and for the sake of simplicity we assume it has only one element and no child component.
My question is: how is Virtual DOM updated in response to the state change?
After a few hours of research, I found two contradictory opinions:
The entire Virtual DOM is torn down; then it is rebuilt from scratch
Only the changed elements are updated in the Virtual DOM.
Unfortunately, official documentation is not clear about this. So, can anybody give the correct answer? (please with supporting reference)
[Edit] : Some parts are imcomplete here, think about reading the comment section !
React update a component when its state or a prop changes. It does a comparison between the previous JSX and the new one and re-render only the differences.
If the parent component has its state or a prop changed, it will be updated.
The child will not be refreshed unless a prop from the parent that is passed to it changes.
Note that the useEffect from the child is triggered first. Knowing that, If you do things that update the state in the child component, then it will be re-rendered everytime (Because the usEffect is triggered everytime if you don't set any dependencies).
Test from Stackblitz - Child Effect is triggered first
Article that made me notice it (I didn't know before this answer :D)
Here is how I understand things in React. To be short, the entire virtual DOM isn't rebuilt from scratch, it's not how JSX comparison works.
I don't have much sources about what I said, but here is the explanation about jsx update from the official documentation. Just that should be enough to eliminate the first point of your list.
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.
I need to update a state in my react app while rendering the content. Is there any option to update state while rendering.
Note: I am very new to react
You can use componentDidUpdate() Lifecycle method.
and inside componentDidUpdate() function update your state.
FYI: componentDidUpdate() calls every time if component gets updtated.
https://reactjs.org/docs/react-component.html
Futureproofing Note: answer relevant to v16.4.
The answer to your question depends on what you mean by saying "while rendering".
When react invokes the render method of a component, it does not really render anything, yet. The results of the render method will undergo "reconciliation", which means that it will be compared to what's rendered at the moment. If a difference is detected, react will actually re-render the DOM, as effectively as it can.
This means that you can not do anything when react is actually rendering / re-rendering the DOM, since your code is not being executed.
You can, however, do something after the render method has been invoked.
As others have stated before me, the componentDidUpdate method is the place.
Note that the fact that this method has been invoked does not necessarily mean react has re-rendered the DOM. It only assures that the component is in sync with the DOM.
Do note that since setting state CAN cause a re-render, so you should really be careful about setting state in componentDidUpdate - or you might cause an infinite loop of sorts.
Even if you correctly implement shouldComponentUpdate, stopping the render cycle from ever reaching cDU- react docs suggest the result of sCU can be taken as a hint in the future, which will break your app in future releases.
So, if you must setState in cDU - you must check that this setState is required, and is not a side-effect of setting the state in the first place.
I have a ScrollToTopOnMount as implemented here with
componentDidMount(prevProps) {
window.scrollTo(0, 0)
}
Which works great, except when I combine this with SSR and hydrating, my page snaps to the top when the hydration happens.
Other answers such as this one gave me hope that there may be some way to discriminate between whether a component is legitimately mounting, or just being hydrated. However, this does not seem to be the case as of react-dom 16.1.1, where both the server and the client appear to call componentWillMount.
Is there a way to have an lifecycle method determine whether it's being mounted where there is no DOM or hydrating an existing node?
ReactDOM.hydrate has a callback when it completes. One solution would be to set and un-set a global var window.hydrating = true and differentiate based on that when the component mounts.