Adding Reusable State to StrictMode #19
Replies: 8 comments 36 replies
-
Is there any documentation for the offscreen API somewhere, such as a blog post or discussion post here? I'm also curious where this falls in the release schedule compared to Concurrent features and server components. Is this a "this feature is imminent so you should turn this on ASAP" or "this is something that will come eventually (more than a year) but you can start preparing for it now if you want" |
Beta Was this translation helpful? Give feedback.
-
I'll emphasize @bvaughn's point here once again, in case someone skims over it in the original post: We rolled out Strict Effects across thousands of components with very little trouble. The few snags we did encounter were in some really complex library code that did lots of manual DOM manipulation. That's why we're confident that we can add this behavior to Strict Mode without imposing an undue burden on developers. Almost everything will just work. This "just work" theme is something you'll see us return to often when discussing React 18 and Concurrent React in general. In the past, we used to be super specific about what is and isn't concurrent "compatible" at a conceptual level. But we realized that by explaining each and every nuance in great detail, we were also coming across as pedantic about things that really don't come up that much in practice. We still need to explain them, because when they do occasionally come up, people need to know how to fix them. But we really just don't think most folks will have that many issues, and when they do the solutions will be relatively straightforward. That's been our experience over the past few years of testing. But we'll see if that trend continues! If we receive feedback that contradicts our findings, we may reevaluate and pick a different strategy. We're confident in our current plans, and we've put lots of thought and research into them. But nothing here is set in stone — that's exactly why we're starting these discussions. |
Beta Was this translation helpful? Give feedback.
-
actual TL;DR: #18 covers this already TL;DR: Reading this makes it sound like you already mentioned the following aspect in internal documents. When I originally read this I had to double check that Strict Effects will work slightly different than double-rendering due to effects having side-effects. Explaining that an effect consists of create and cleanup method and that you invoke create and cleanup in this mode might avoid some confusion. Given React.useEffect(function create() {
let cancelled = false;
fetch(url).then(data => {
if (!cancelled) setData(data);
})
return function cleanup() {
cancelled = true;
}
}, []) Strict Effects won't just result in fetching twice. Strict Effects will also call the cleanup inbetween double invokation to make sure that the cleanup actually cleans up the effect and doesn't leave some side-effects. What I mean is that one might think that Strict Effect means create();
+create(); when it actually means +const cleanup = create();
+cleanup();
create(); |
Beta Was this translation helpful? Give feedback.
-
Partial hydration named somewhere else really got me thinking. One thing I saw in the tests was hydration based on hovering. If I understand correctly, the upcoming Offscreen api is currently made for being able to unmount something and then remount it again with the same state it had before. But I'm wondering whether using it to "pre-render" components that haven't been rendered is also possible. I could see a router starting to render a page when a user hovers over a link to it. I guess you could do this already by making it invisible, but now with concurrent it seems like it would be possibly more preformant. This all is quite messy in my head, but I remember seeing useTransition with data loading to preload data for tabs not clicked on, but you wouldn't want to keep all state for a page in a router above the actual page. Pages tend to have very different state and state nested different components and so on. Ideally I think I'd want to: render hidden on hover, use isPending on the menu item if it takes a long time to load, and offscreen it again when clicking on another route. Guess my question is really, is Offscreen only for unmounting already rendered items or can it aslo be used to prerender things and how does all that tie into the other new APIs? |
Beta Was this translation helpful? Give feedback.
-
What about console.logs when reinvoking strict effects? Currently, React mutes the additional console.logs when rerendering within the StrictMode - would the same happen here? Note that I actually consider muting those console.logs having a negative impact on the overall DX. I find it too magical and I've already seen dozen of seasoned devs being tripped by this because they were not aware of this which lead to some serious hair pulling as the observed logs didn't match other observations they have made about the code execution at the same time 😉 If anything - I would switch the default and make console.log muting an opt-in through React DevTools. If you plan on muting them when reinvoking strict effects then probably even more people will start getting confused about this because this won't match a variety of other observable side-effects in the browser, like even the amount of requests in the Network panel of browser devtools. |
Beta Was this translation helpful? Give feedback.
-
Dan said over in #98 (comment) :
I'm not sure I saw this come up specifically previously. Does anyone have a reproduction, and if so, can we get it linked over in reduxjs/react-redux#1732 ? |
Beta Was this translation helpful? Give feedback.
-
Framer Motion's <AnimatePresence>
{isVisible ? <motion.div exit={{ opacity: 0 }} /> : null}
</AnimatePresence> This animation is triggered by a However, a breaking change in React 18 is that Given this, how am I recommended to port this component to React 18? |
Beta Was this translation helpful? Give feedback.
-
I was looking for an exact definition or a document that describes what a Reusable State is, but I didn't find it. I read the documentation on Strict Mode and Discussion "18 How to support Reusable State in Effects" did not find an exact definition there. Which state exactly will be reusable. Is this a State created using useState and useRef? Or some other. |
Beta Was this translation helpful? Give feedback.
-
Overview
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.Why is React adding reusable state?
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 StrictMode
If 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.Related posts
For more information on supporting reusable state in StrictMode, see:
Beta Was this translation helpful? Give feedback.
All reactions