-
Notifications
You must be signed in to change notification settings - Fork 24.3k
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
VirtualizedList CellRenderer optimization #21163
VirtualizedList CellRenderer optimization #21163
Conversation
Thank you for your pull request and welcome to our community. We require contributors to sign our Contributor License Agreement, and we don't seem to have you on file. In order for us to review and merge your code, please sign up at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need the corporate CLA signed. If you have received this in error or have any questions, please contact us at cla@fb.com. Thanks! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
eslint
found some issues. You may run yarn prettier
or npm run prettier
to fix these.
getItemCount={data => data.length} | ||
/>, | ||
); | ||
const cell = virtualList._cellRefs['i4']; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dot-notation: ["i4"] is better written in dot notation.
Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Facebook open source project. Thanks! |
Thanks for adding tests and @matthargett for approving this. I'll import this and put it on the team's review queue, as an additional precaution. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hramos has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.
Sorry for the delay - thanks for making this change. Did you test to make sure that sticky headers in RNTester still work? |
No unfortunately I didn’t. I’ll check and report the results. But I checked all the list files for additional props and didn’t find any other references. Does the sticky headers implemention need CellRenderer to re-render? |
When this patch is applied, I get a RedScreen on RNTester, specifically on VirtualizedList, VirtualizedSectionList.js:201: |
Thanks @hramos I just booted up RNTester my self and got the same error. My bad for only testning this on FlatList and SectionLists without Sticky Headers. |
@sahrens I'm I correct that the problems lays in the fact that |
@hramos can I do anything to help speed up the review? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hramos has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.
@christianbach thanks for adding the tests, I'm merging this. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks for adding a unit test!
@hramos any updates? 😸 |
if (child.props.onLayout) { | ||
child.props.onLayout(event); | ||
if (child.props._onLayout) { | ||
child.props._onLayout(event); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are calling component function _onLayout
which is not available from child.props
.
I think this line should look like this:
child.props._onLayout(event); | |
child.props.onLayout(event, child.props.cellKey, child.props.index); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @sebryu and thanks for reviewing this!
_onLayout
is available and is part of the rewrite of CellRenderer to stop functions being created on every render
_onLayout = (e): void =>
this.props.onLayout &&
this.props.onLayout(e, this.props.cellKey, this.props.index);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hello @christianbach !
I read your code, and I noticed _onLayout function in CellRenderer class. However - you are trying to call it as child.props._onLayout
, and simply - _onLayout
function is not accessible from child.props
. You would need to have ref to CellRenderer to call its functions.
So in this case, my suggested changes should work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My bad and thanks! I will change it straight away!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Finally had the time to do a commit. Tested the change in our app without issues.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sebryu Do I need to do anything else, perhaps rebase from master?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @christianbach !
You have to ping RN guys, I just wanted to create similar issue, so I reviewed your code.
It looks ok for me though 👍
This comment has been minimized.
This comment has been minimized.
|
@christianbach There's a conflict once again. Would you mind fixing it? Also, as soon as the conflict is resolved, can we merge this one or are we missing anything here? |
@derekstavis sure, will try to find some time this week. |
The reason we haven't landed this change yet is mainly because nobody at FB has volunteered to ensure that this won't break various surfaces. We have more than 1000 screens written using React Native and this change can potentially break any number of them by not updating things properly and we have no way of automatically detecting it :( |
How about make this optimization an option but not enabled by default. |
If we don't change the default behavior this will be much easier to land, yes! |
I tried my hand at not making this the default behavior and accepting an additional prop |
Can't anyone add |
@christianbach just wanted to say that I admire your tenacity to get this PR merged (after close to a year!), and I am really looking forward to it! Many thanks for putting in the effort. |
I was able to contain most of the changes to be opt-in and not be the default behavior. Though this has a bit of hacky JS context trickery which might make it brittle. If acceptable, would like some guidance on how to write some good tests so that this does not break easily. let virtualizedList = this;
this._onCellLayout = function (e) {
virtualizedList._onCellLayoutGen(e, this.cellKey, this.index)
} |
This PR is beautiful. I hope this gets merged. CellRenderer re-renders is one of the massive bottlenecks for our performance where we have a VirtualizedList with over 4000 rows. Thank you everyone who is involved in this PR. Hope I can help here too. |
@azizhk If that is too diffcult, how about something like this:
|
@sunnylqm Thanks. We also have changes in ScrollViewStickyHeader.js so for that we would need to make changes in a lot of Components (like ScrollView). That gave me a different idea, wrapping inside an isExperimentalOptimization Context Provider. Something like:
This can be passed down to ScrollViewStickyHeader. Let me whip up something over the weekend which agrees to the "not changing the default behavior" requirement. |
I have an idea to get this merged: -const { extraData } = this.props
+const { extraData = {} } = this.props By changing the default value of If the user wants to enable the optimization they just need to pass Ideally this would not be necessary, but since internal facebook code is blocking the merge of this pull request this may be a good middle ground solution. No facebook code change necessary while still allowing people to opt in. |
I was able to retain the default behavior for onLayout as well: master...azizhk:exp_context_virtuallist @cpojer Let me know if looks good and I'll create a PR and add tests. |
Perhaps it's time to close this PR? |
Alright, I'll close it but I still think it is valueable to make this change in a backwards-compatible way – I'm fine with introducing new props with new names that we can migrate people to. |
@cpojer There is a change in a backwards-compatible way, please review master...azizhk:exp_context_virtuallist |
@cpojer @christianbach I've created my PR: #27115. Hoping we can continue the discussion there. Please close it, if it does not seem viable. |
Summary: This sync includes the following changes: - **[f7cdc8936](facebook/react@f7cdc8936 )**: Also turn off enableSyncDefaultUpdates in RN test renderer ([#21293](facebook/react#21293)) //<Ricky>// - **[4c9eb2af1](facebook/react@4c9eb2af1 )**: Add dynamic flags to React Native ([#21291](facebook/react#21291)) //<Ricky>// - **[9eddfbf5a](facebook/react@9eddfbf5a )**: [Fizz] Two More Fixes ([#21288](facebook/react#21288)) //<Sebastian Markbåge>// - **[11b07597e](facebook/react@11b07597e )**: Fix classes ([#21283](facebook/react#21283)) //<Sebastian Markbåge>// - **[96d00b9bb](facebook/react@96d00b9bb )**: [Fizz] Random Fixes ([#21277](facebook/react#21277)) //<Sebastian Markbåge>// - **[81ef53953](facebook/react@81ef53953 )**: Always insert a dummy node with an ID into fallbacks ([#21272](facebook/react#21272)) //<Sebastian Markbåge>// - **[a4a940d7a](facebook/react@a4a940d7a )**: [Fizz] Add unsupported Portal/Scope components ([#21261](facebook/react#21261)) //<Sebastian Markbåge>// - **[f4d7a0f1e](facebook/react@f4d7a0f1e )**: Implement useOpaqueIdentifier ([#21260](facebook/react#21260)) //<Sebastian Markbåge>// - **[dde875dfb](facebook/react@dde875dfb )**: [Fizz] Implement Hooks ([#21257](facebook/react#21257)) //<Sebastian Markbåge>// - **[a597c2f5d](facebook/react@a597c2f5d )**: [Fizz] Fix reentrancy bug ([#21270](facebook/react#21270)) //<Sebastian Markbåge>// - **[15e779d92](facebook/react@15e779d92 )**: Reconciler should inject its own version into DevTools hook ([#21268](facebook/react#21268)) //<Brian Vaughn>// - **[4f76a28c9](facebook/react@4f76a28c9 )**: [Fizz] Implement New Context ([#21255](facebook/react#21255)) //<Sebastian Markbåge>// - **[82ef450e0](facebook/react@82ef450e0 )**: remove obsolete SharedArrayBuffer ESLint config ([#21259](facebook/react#21259)) //<Henry Q. Dineen>// - **[dbadfa2c3](facebook/react@dbadfa2c3 )**: [Fizz] Classes Follow Up ([#21253](facebook/react#21253)) //<Sebastian Markbåge>// - **[686b635b7](facebook/react@686b635b7 )**: Prevent reading canonical property of null ([#21242](facebook/react#21242)) //<Joshua Gross>// - **[bb88ce95a](facebook/react@bb88ce95a )**: Bugfix: Don't rely on `finishedLanes` for passive effects ([#21233](facebook/react#21233)) //<Andrew Clark>// - **[343710c92](facebook/react@343710c92 )**: [Fizz] Fragments and Iterable support ([#21228](facebook/react#21228)) //<Sebastian Markbåge>// - **[933880b45](facebook/react@933880b45 )**: Make time-slicing opt-in ([#21072](facebook/react#21072)) //<Ricky>// - **[b0407b55f](facebook/react@b0407b55f )**: Support more empty types ([#21225](facebook/react#21225)) //<Sebastian Markbåge>// - **[39713716a](facebook/react@39713716a )**: Merge isObject branches ([#21226](facebook/react#21226)) //<Sebastian Markbåge>// - **[8a4a59c72](facebook/react@8a4a59c72 )**: Remove textarea special case from child fiber ([#21222](facebook/react#21222)) //<Sebastian Markbåge>// - **[dc108b0f5](facebook/react@dc108b0f5 )**: Track which fibers scheduled the current render work ([#15658](facebook/react#15658)) //<Brian Vaughn>// - **[6ea749170](facebook/react@6ea749170 )**: Fix typo in comment ([#21214](facebook/react#21214)) //<inokawa>// - **[b38ac13f9](facebook/react@b38ac13f9 )**: DevTools: Add post-commit hook ([#21183](facebook/react#21183)) //<Brian Vaughn>// - **[b943aeba8](facebook/react@b943aeba8 )**: Fix: Passive effect updates are never sync ([#21215](facebook/react#21215)) //<Andrew Clark>// - **[d389c54d1](facebook/react@d389c54d1 )**: Offscreen: Use JS stack to track hidden/unhidden subtree state ([#21211](facebook/react#21211)) //<Brian Vaughn>// - **[c486dc1a4](facebook/react@c486dc1a4 )**: Remove unnecessary processUpdateQueue ([#21199](facebook/react#21199)) //<Sebastian Markbåge>// - **[cf45a623a](facebook/react@cf45a623a )**: [Fizz] Implement Classes ([#21200](facebook/react#21200)) //<Sebastian Markbåge>// - **[75c616554](facebook/react@75c616554 )**: Include actual type of `Profiler#id` on type mismatch ([#20306](facebook/react#20306)) //<Sebastian Silbermann>// - **[1214b302e](facebook/react@1214b302e )**: test: Fix "couldn't locate all inline snapshots" ([#21205](facebook/react#21205)) //<Sebastian Silbermann>// - **[1a02d2792](facebook/react@1a02d2792 )**: style: delete unused isHost check ([#21203](facebook/react#21203)) //<wangao>// - **[782f689ca](facebook/react@782f689ca )**: Don't double invoke getDerivedStateFromProps for module pattern ([#21193](facebook/react#21193)) //<Sebastian Markbåge>// - **[e90c76a65](facebook/react@e90c76a65 )**: Revert "Offscreen: Use JS stack to track hidden/unhidden subtree state" ([#21194](facebook/react#21194)) //<Brian Vaughn>// - **[1f8583de8](facebook/react@1f8583de8 )**: Offscreen: Use JS stack to track hidden/unhidden subtree state ([#21192](facebook/react#21192)) //<Brian Vaughn>// - **[ad6e6ec7b](facebook/react@ad6e6ec7b )**: [Fizz] Prepare Recursive Loop for More Types ([#21186](facebook/react#21186)) //<Sebastian Markbåge>// - **[172e89b4b](facebook/react@172e89b4b )**: Reland Remove redundant initial of isArray ([#21188](facebook/react#21188)) //<Sebastian Markbåge>// - **[7c1ba2b57](facebook/react@7c1ba2b57 )**: Proposed new Suspense layout effect semantics ([#21079](facebook/react#21079)) //<Brian Vaughn>// - **[316aa3686](facebook/react@316aa3686 )**: [Scheduler] Fix de-opt caused by out-of-bounds access ([#21147](facebook/react#21147)) //<Andrey Marchenko>// - **[b4f119cdf](facebook/react@b4f119cdf )**: Revert "Remove redundant initial of isArray ([#21163](facebook/react#21163))" //<Sebastian Markbage>// - **[c03197063](facebook/react@c03197063 )**: Revert "apply prettier ([#21165](facebook/react#21165))" //<Sebastian Markbage>// - **[94fd1214d](facebook/react@94fd1214d )**: apply prettier ([#21165](facebook/react#21165)) //<Behnam Mohammadi>// - **[b130a0f5c](facebook/react@b130a0f5c )**: Remove redundant initial of isArray ([#21163](facebook/react#21163)) //<Behnam Mohammadi>// - **[2c9fef32d](facebook/react@2c9fef32d )**: Remove redundant initial of hasOwnProperty ([#21134](facebook/react#21134)) //<Behnam Mohammadi>// - **[1cf9978d8](facebook/react@1cf9978d8 )**: Don't pass internals to callbacks ([#21161](facebook/react#21161)) //<Sebastian Markbåge>// - **[b9e4c10e9](facebook/react@b9e4c10e9 )**: [Fizz] Implement all the DOM attributes and special cases ([#21153](facebook/react#21153)) //<Sebastian Markbåge>// - **[f8ef4ff57](facebook/react@f8ef4ff57 )**: Flush discrete passive effects before paint ([#21150](facebook/react#21150)) //<Andrew Clark>// - **[b48b38af6](facebook/react@b48b38af6 )**: Support nesting of startTransition and flushSync (alt) ([#21149](facebook/react#21149)) //<Sebastian Markbåge>// Changelog: [General][Changed] - React Native sync for revisions c9aab1c...f7cdc89 jest_e2e[run_all_tests] Reviewed By: rickhanlonii Differential Revision: D27740113 fbshipit-source-id: 6e27204d78e3e16ed205170006cb97c0d6bfa957
Summary: This is VirtualList Optimization done by christianbach in facebook#21163 enabled behind a prop: `experimentalVirtualizedListOpt` with backward compatibility. Fixes facebook#20174 based of jasekiw pull request facebook#20208 ## Changelog // TODO: [CATEGORY] [TYPE] - Message Pull Request resolved: facebook#27115 Test Plan: // TODO: Add tests related to backward compatibility. (Will need help) Differential Revision: D30095387 Pulled By: lunaleaps fbshipit-source-id: 1c41e9e52beeb79b56b19dfb12d896a2c4c49529
Fixes
Fixes #20174 based of @jasekiw pull request #20208
Changes
Test Plan:
Tested on a real device with why-did-you-update running to view when the CellRendere component re-rendered. Applied updates to the list items to make sure they still get updated.
Added a test that makes sure the initial onLayout is still called properly.
Release Notes:
[GENERAL] [ENHANCEMENT][VirtualList] - Prevent unnecessary re-renders of the CellRenderer Component.