lazy loading is useless right now
See original GitHub issueLazy loading should make mounting the swipeable views component faster by only mounting not initially visible views later, but how lazy loading is implemented right now is that, on initial render only view of the chosen index is rendered – this is correct --, but on componentDidMount event, it sets the firstRender state to false, which triggers a synchronous re-render of this component. In this re-render, all the other views are mounted too. This makes lazy-loading completely useless, because in the end, all the views are rendered synchronously.
reference: https://reactjs.org/docs/react-component.html#componentdidmount
You may call setState() immediately in componentDidMount(). It will trigger an extra rendering, but it will happen before the browser updates the screen. This guarantees that even though the render() will be called twice in this case, the user won’t see the intermediate state. Use this pattern with caution because it often causes performance issues. In most cases, you should be able to assign the initial state in the constructor() instead. It can, however, be necessary for cases like modals and tooltips when you need to measure a DOM node before rendering something that depends on its size or position.
- I have searched the issues of this repository and believe that this is not a duplicate.
Expected Behavior
views not initially visible should be mounted asynchronously with either raf
or setTimeout
.
Current Behavior
views not initially visible are still mounted synchronously after componentDidMount event.
Steps to Reproduce (for bugs)
This can be observed in demo.
- go to demo page, https://n075p8mq7l.codesandbox.io/
- open chrome dev tools
- click “start profiling and reload page”
- check the user timing section and see that “Lifecycle hook scheduled a cascading update”, and all the views are mounted in the cascading update synchronously.
Context
When you put something huge into the views, performance is not acceptable without lazy loading.
Your Environment
Tech | Version |
---|---|
react-swipeable-views | 0.12.16 |
React | 16.4.1 |
platform | macOS |
etc |
Issue Analytics
- State:
- Created 5 years ago
- Reactions:5
- Comments:22 (3 by maintainers)
Normally React wants to keep the UI consistent with what you told it to render. So if you render
then it will have to call render methods and create DOM nodes for the whole trees of
Expensive1
,Expensive2
, andExpensive3
, whenSlideshow
is being mounted.However, maybe you’re only showing
Expensive1
on the first render, andExpensive2
andExpensive3
are not immediately visible. Like if this is really a side show.Today, it’s idiomatic to render just the current node, e.g.
The upside of this is that now your first render of
<Slideshow current={0} />
only includesExpensive1
. However, the downside is that the moment you switch to<Slideshow current={1} />
, you will experience jank from creating and rendering the wholeExpensive2
tree.Ideally what we want to do is to tell React that when we render
<Slideshow current={0} />
, we want to showExpensive1
, but we want to start preparingExpensive2
when the browser is idle. This way it won’t block the initial render or cause jank, but by the time you click “next”Expensive2
might just already be complete, and in this case it’ll just replace the DOM node.This is exactly what time slicing which I partially demoed in my talk will allow. Your code might look something like:
Note that
hidden
is a real HTML attribute. (It acts similar todisplay: none
.) But it could also serve as a hint to React that the tree inside it doesn’t actually need to be committed immediately — because it’s not visible anyway. Also note: this is not a final API, I’m just explaining what it lets you do.So when you render
<Slideshow current={0} />
, React mountsand whenever the browser is idle (e.g. when you look at the first slideshow item), it will start working on
Expensive2
in small chunks. When it’s ready, it will just append it to the hidden div — which won’t be observable to the user because it’s hidden.Now if you switch to
<Slideshow current={1} />
, React doesn’t need to do any extra rendering because it has already “prepared”Expensive2
ahead of time.And if you switch too fast, and React hasn’t been able to fully prepare
Expensive2
yet, it will just pick it up where it left off but set a much more aggressive deadline since we want to see results within ~150ms.I hope this explains it a bit! The crucial part here is time slicing. This optimization wouldn’t make sense if pre-rendering
Expensive2
orExpensive3
blocked the thread since in this case it wouldn’t be worth slowing down the interactions whileExpensive1
is visible. But thanks to React’s architecture, we can actually start pre-renderingExpensive2
andExpensive3
in small chunks without blocking the thread, and that’s what will enable this optimization.We’re going to have a first-class API for mounting invisible components asynchronously without blocking the thread in the future, right inside React. Stay tuned 😃