-
Notifications
You must be signed in to change notification settings - Fork 45.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
React.lazy remounts component if used within a render #14299
Comments
This is how regular React works. function Routing() {
<>
<Route path="foo" component={function Foo() { /* ... */ }} />
</>
} has exactly the same issues. It's always bad practice. If you feel concrete suggestions on whether this could be better documented, please feel free to send a doc PR. |
Yes, I understand why it's happening, but cannot it be improved? We are heading toward function components that are using memoization and in my opinion, it won't be that uncommon that people would try and possibly want to define resources within a component to improve colocation. Your example with defining a component within a render can be tackled with I think it's a slightly different case and it could use some second thought. |
I did another example based on Alpha and with I am not trying to convince you that this is a feature that must be. It's just a suggestion what might be interesting to tackle in the future. I am sure there might be several reasons I just don't see, especially the complexity of Suspense still kinda eludes me. I will send the docs PR soon-ish. |
I reckon - it never should work. You are giving a new type to React, it should unmount the old one, and mount a new one. This is NOT a remount. |
But how do you explain it's possible to memoize regular component, but not the lazy one? That's what memoize should do, right? Keep the same object over renders. |
Ok wow, nevermind, fresh look after some time and I did spot a problem right away... const LazyLoadedMemo = React.useMemo(() => LazyLoaded); I am missing the second arg to Disclaimer: Nonetheless, since |
@ashkank83 Some dirty solution is the following, but I don't like global variables, so I don't recommend doing that. Also, I can imagine it would be suffering from race conditions. Otherwise, I don't think there a better solution that's officially supported. const cached = {}
function DynamicLoader(props) {
if (!cached[props.component]) {
const LazyComponent = React.lazy(() => import(`${props.component}`))
cached[props.component] = (
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
)
}
return cached[props.component]
} |
@FredyC Thanks for the reply. Since posting the question, I got the following as well as a potential solution const DynamicLoader = React.memo(function(props: any) {
const LazyComponent = React.lazy(() => import(`${props.component}`));
return (
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
);
}); Not sure if there are cons and pro to the different solutions. As you said as well, I prefer to avoid the global variable, so maybe React.memo is prefered. What do you think? |
@ashkank83 That's probably better, but it depends how permanent is a cache of
Sounds risky enough to me. I don't know, I've given up on that as it's not that horrible to do it recommended with manual calls of |
For the benefit of others reading this, an approach to persisting the lazy references As FredyC refers to in previous comments, this has benefits over Using global module state (as in Fredy's above comment) is possibly preferable to |
@gaearon any insights on this? I considered React.lazy to have the same behavior as a regular react component. Meaning that it will be "rendered" equally as other Components. But the Component instance changes after every render. |
Again - this is how it should work. |
I’m experiencing a similar issue with This in turn comes from how Now, if only |
Of course it never will do it - there is no way to know what is "behind import" before calling it. Use "SSR-friendly" code splitting libraries to "track" "loaded" "bundles" |
So what I’m reading between the snark here is that I’m not concerned with SSR for the moment, why are you recommending to use SSR techniques to solve a problem you said isn’t a problem and isn’t solvable in the sentence before? |
Because you are missing the point @Haraldson.
For example - So, please, just try to understand how it works, why it works that way, and why it's not working for your case, and what's is wrong with your case. |
Implementation:
Usage :
|
Solution from @maulik-soni works for me if I wrap the output in const CodesIndex = useMemo(() => LazyloadComponent(() => import('./Screens/Codes'))(true), []);
return (
<Route path="/codes" components={CodesIndex} />
); |
Might I suggest another wrapper option, building on @maulik-soni's work? Use a closure to retain the reference to the to-be-built component: const buildLazyComponent = (load, fallback = null) => {
let LoadedComponent = null;
const LazyComponent = forwardRef(
(props, ref) => {
if (!LoadedComponent) {
LoadedComponent = lazy(load);
}
return (
<Suspense fallback={fallback}>
<LoadedComponent {...props} ref={ref} />
</Suspense>
)
}
);
return LazyComponent;
} Usage: const MyComponent = buildLazyComponent(
() => import('./MyComponent.lazy.jsx'), // or whatever convention lets you distinguish
<Loading />
);
export default MyComponent; Edited to include |
lazy with forwardRef and memo 🙃:great wrapper I was trying to use lazy component with forwardedRef component at the same time from a while @landisdesign
usage:
just a note the someComponent should be wrapped on forwardedRef as well if you wish to use the ref and also it can be wrapped with memo with no problems 😊😊😊😊 component example '.someComponent.jsx' :
|
@ahmed-zhran oops! I forgot that part! Good catch. I'll edit my attempt above to put it all in one place. |
Do you want to request a feature or report a bug?
Bug
What is the current behavior?
Simple reproduction here: https://codesandbox.io/s/6z4qmm2k7n
The contrived example does not make much sense, but I got burned by this in the context of react-router. I simply wanted to avoid defining all lazily loaded components up front but instead have them coupled with a particular route.
What is the expected behavior?
In case this is intended behavior, it should be definitely documented. Otherwise it would be nice to have some memoized lazy variant. Possibly with hooks coming what about
useLazy
? :)The text was updated successfully, but these errors were encountered: