Strict mode was released with React 16.3 as tool to identify coding patterns that may cause problems with React’s (then experimental) concurrent rendering APIs. Adding <StrictMode>
to a React application adds special behavior (only in DEV mode) to all of components it wraps around. For example, when running in “strict mode“ React will intentionally double-render components for in order to flush out unsafe side effects.
With the release of React 18, StrictMode
gets an additional behavior to ensure it's compatible with reusable state. When StrictMode is enabled, React intentionally double-invokes effects (mount
-> unmount
-> mount
) for newly mounted components. Like other strict mode behaviors, React will only do this for development builds.
There are multiple features we'd like to add to React that have the same constraint: a component needs to be resilient to being "mounted" and "unmounted" more than once.
The first such feature is already added — it's Fast Refresh. It's enabled by default in Next.js, Create React App, and React Native. With Fast Refresh, every time you save a file, your effects re-run. This means that if a component or a library breaks because of occasionally re-running its effects, it won't work with Fast Refresh well.
Other features we're building rely on the same constraint. For example, we are working on a new “Offscreen” API that will enable us to better support UIs like tabbed containers and virtualized lists and to make better use of new browser APIs like content-visibility. (It will also help with the pre-rendering optimizations the React Native team is working on.) But to get there we need to make changes to the way effects work.
Let’s say you have a component that gets rendered conditionally — for example, the current tab. If this component or any of its descendants have state inside of them, that state will be lost when the component is unmounted. Until now, the only way to preserve the state has been to “lift” it out into a higher component (or an external store like Redux).
The main motivation for the new Offscreen API (and the effects changes described in this post) is to allow React to preserve state like this by hiding components instead of unmounting them. To do this React will call the same lifecycle hooks as it does when unmounting– but it will also preserve the state of both React components and DOM elements.
This means that components may “mount” and “unmount” more than once.
When a component is hidden– whether it’s in a tabbed container or a virtualized list– it is no longer visible in the DOM, just like if it was unmounted (removed) from the DOM. It isn’t actually unmounted (because we want to preserve the state) but from a user’s perspective they are the same.
It wouldn’t make sense for an unmounted component to trigger some imperative code (e.g. to position a tooltip). The same is true for a component that’s been hidden. So React needs to tell the component that it is being hidden. How? By calling the same functions that it calls when the component is unmounted (either an effect cleanup functions or componentWillUnmount
).
When a component get shown again after being hidden– it’s similar to being mounted again except that it will have all of its previous state. React will tell the component that it is being shown again by calling the same functions it calls on mount (either an effect or componentDidMount
).
We've found that the majority of product code "just works" with this approach but some tweaks are necessary.
Opting out of StrictModeIf double invoking effects cause significant problems for your app, you can disable <StrictMode>
for it entirely until you're able to fix them. There is currently no way to keep the old <StrictMode>
behavior—if you enable it, it will include double invoking effects.
For more information on supporting reusable state in StrictMode, see:
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4