-
Notifications
You must be signed in to change notification settings - Fork 482
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
lazy loading is useless right now #453
Comments
@oliviertassinari Can I get some explanations? |
@Bobgy You are right. Thank you for reporting the issue. Right now, the lazy loading only works with server side rendering. Using a |
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 :-) |
@gaearon I'm happy to hear that, it will make this implementation a lot simpler.
Is the new first-class API able to handle that (particularly, mount heavy invisible children at the correct timing without causing jank)? just guessing probably with some requestIdleCallback magic? |
Normally React wants to keep the UI consistent with what you told it to render. function Slideshow() {
return (
<div>
<Expensive1 />
<Expensive2 />
<Expensive3 />
</div>
);
} then it will have to call render methods and create DOM nodes for the whole trees of However, maybe you're only showing Today, it's idiomatic to render just the current node, e.g. function Slideshow(props) {
return (
<div>
{props.current === 0 && <Expensive1 />}
{props.current === 1 && <Expensive2 />}
{props.current === 2 && <Expensive3 />}
</div>
);
} The upside of this is that now your first render of Ideally what we want to do is to tell React that when we render This is exactly what time slicing which I partially demoed in my talk will allow. Your code might look something like: function Slideshow(props) {
return (
<div>
<div hidden={props.current !== 0}>
<Expensive1 />
</div>
<div hidden={props.current !== 1}>
<Expensive2 />
</div>
<div hidden={props.current !== 2}>
<Expensive3 />
</div>
</div>
);
} Note that So when you render <div>
<div hidden={false}>
<Expensive1 />
</div>
<div hidden={true}>
<!-- not ready yet -->
</div>
<div hidden={true}>
<!-- not ready yet -->
</div>
</div> and whenever the browser is idle (e.g. when you look at the first slideshow item), it will start working on <div>
<div hidden={false}>
<Expensive1 />
</div>
<div hidden={true}>
<!-- not visible, but prepared during idle periods and now ready! -->
<Expensive2 />
</div>
<div hidden={true}>
<!-- not ready yet, but React will start working on it next -->
</div>
</div> Now if you switch to And if you switch too fast, and React hasn't been able to fully prepare I hope this explains it a bit! The crucial part here is time slicing. This optimization wouldn't make sense if pre-rendering |
cool beans, but doesn't this break instances where a component gets initial state from a prop and is rendered before they are expected? |
@demiacle I don't understand the question. Can you explain in more detail? |
Hmm just double checked for sanity, It's discouraged in the docs on constructor so its probably an anti pattern, but it does have a use case. I was playing around with it here. Essentially with your proposed solution, clicking the button wouldn't change any text because the state was already derived from the props. |
Will this change the way we approach route/page transitions in React? |
@demiacle If the "use case" you're referring to is capturing props at the time constructor was called then yes, you might capture them earlier than intended if you use pre-rendering. I'm struggling to understand why you'd want a component API like this since it's pretty unintuitive that changing a prop has no effect later. This is why we discourage it. I wouldn't say pre-rendering would "break" this — it's just that you need to decide whether you want pre-rendering or you want this behavior (which is fundamentally incompatible with the idea of pre-rendering). @Raigasm It might (note you won't have to use it but might find it beneficial). We'll see. |
What I'm wondering is if this has applications beyond the case where all the components are expensive. a la your example:
|
With programming its never about the why though haha, if it can happen it will. I was only interested in being explicit because in the example I presented, there are 2 identical components that are rendered differently. Just thought it should be noted. Anyways super cool stuff! |
I'm using the word "expensive" to better get the point across. It doesn't have to literally be expensive — no matter how fast your rendering is, it would still be faster to do it earlier in an idle period (and then switch instantly). |
Dan's example reminded me of tabs and how the content of active one is shown only... "Ты суслика видишь? И я не вижу. А он есть" |
This is very cool. Purely speculating here without a solid use case: would there be a way to specify rendering priority? As per the above description it seems like the background rendering is perhaps prioritised by the order in which components are specified, or just spread across all equally in some piecemeal way, but I imagine that it may be the case in a more complex layout that one hidden component that is definitely going to be the next thing the user sees should be prioritised over some other hidden component which may have lesser importance (e.g. less important for it to be jankless, or it is suspected to be more likely to come into play later, or perhaps very few people even reveal that component, etc) I can imagine this being useful when you have a primary user pathway which can be optimised to have the best jank-free experience, when there exists less important, but potentially very expensive to render hidden components off to the side |
It's pretty early to see right now. We'll see how it evolves after we use it for a while. |
@demiacle I was thinking at the same thing as you. With this api we could have both benefits if we split the component that needs the initial state from a prop into a wrapper that just computes/stores this state and a child that is used inside a This would help me with a few "render only when visible" cases where content is not reliant on any API data. class ExpensiveOnDemand extends React.Component {
constructor (props) {
super(props);
// do stuff with state and initial props
this.state = { ... };
}
render (props) {
return (
<div hidden={!this.props.visible}>
<ExpensiveStuff {...this.props} {...this.state}/>
// or maybe something with this.props.children
</div>
);
}
} This will probably need more work for sub components that fetch data on |
In our app, we generally would not have rendered Expensive2 or 3. Should we be adopting a technique where they are hidden, for instance with DOM or CSS attributes? |
@gaearon is there a plan for an opt-in component like |
@ChrisLincoln No, my explanation is about a future feature of React. It doesn't exist today and won't work for you the way I described. @josgraha I don't know, we'll think more about this before finalizing the API. |
@gaearon how about the name <div>
{list.map((item, index) => (
<div key={item.id} defer={index !== current_index}>{item.content}</div>
))}
</div> |
I am using swipeable to swipe gvis charts. The json data for each chart I fetch from my S3 API each time a swipe occurs. To solve the problem when it's not ready I have a loading spinner like this:
It does not fetch all the json at the same time from my API, but it renders the chart component for all items . So it seems to work. I will see how 230 items work or if it's get to slow. |
Lazy 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
Expected Behavior
views not initially visible should be mounted asynchronously with either
raf
orsetTimeout
.Current Behavior
views not initially visible are still mounted synchronously after componentDidMount event.
Steps to Reproduce (for bugs)
This can be observed in demo.
Context
When you put something huge into the views, performance is not acceptable without lazy loading.
Your Environment
The text was updated successfully, but these errors were encountered: