Skip to content
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

useMutableSource hook #18000

Merged
merged 27 commits into from
Mar 11, 2020
Merged

useMutableSource hook #18000

merged 27 commits into from
Mar 11, 2020

Conversation

bvaughn
Copy link
Contributor

@bvaughn bvaughn commented Feb 7, 2020

useMutableSource() enables React components to safely and efficiently read from a mutable external source in Concurrent Mode. The API will detect mutations that occur during a render to avoid tearing and it will automatically schedule updates when the source is mutated.

RFC: reactjs/rfcs#147

@facebook-github-bot facebook-github-bot added CLA Signed React Core Team Opened by a member of the React Core Team labels Feb 7, 2020
@codesandbox-ci
Copy link

codesandbox-ci bot commented Feb 7, 2020

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Latest deployment of this branch, based on commit dee1164:

Sandbox Source
happy-clarke-zo2tp Configuration

@sizebot
Copy link

sizebot commented Feb 7, 2020

Details of bundled changes.

Comparing: 30a998d...dee1164

react-dom

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
ReactDOM-profiling.js +1.3% +1.5% 422.63 KB 428.22 KB 76.63 KB 77.79 KB FB_WWW_PROFILING
react-dom-server.browser.development.js +0.3% +0.4% 135.4 KB 135.83 KB 34.82 KB 34.97 KB UMD_DEV
react-dom-server.browser.production.min.js 🔺+0.3% 🔺+0.2% 20.1 KB 20.16 KB 7.44 KB 7.46 KB UMD_PROD
react-dom-unstable-native-dependencies.development.js 0.0% -0.0% 55.92 KB 55.92 KB 14.48 KB 14.47 KB NODE_DEV
react-dom.development.js +1.3% +1.1% 892.49 KB 903.66 KB 196.69 KB 198.95 KB UMD_DEV
react-dom-unstable-native-dependencies.production.min.js 0.0% -0.1% 10.04 KB 10.04 KB 3.37 KB 3.36 KB NODE_PROD
react-dom.production.min.js 🔺+1.3% 🔺+1.5% 117.17 KB 118.67 KB 37.52 KB 38.09 KB UMD_PROD
react-dom.profiling.min.js +1.2% +1.3% 120.88 KB 122.38 KB 38.77 KB 39.29 KB UMD_PROFILING
react-dom.development.js +1.3% +1.1% 849.35 KB 860.02 KB 194.22 KB 196.44 KB NODE_DEV
react-dom-server.node.development.js +0.3% +0.4% 129.68 KB 130.09 KB 34.64 KB 34.77 KB NODE_DEV
react-dom.production.min.js 🔺+1.3% 🔺+1.4% 117.24 KB 118.75 KB 36.79 KB 37.31 KB NODE_PROD
react-dom-server.node.production.min.js 🔺+0.3% 🔺+0.2% 20.43 KB 20.48 KB 7.55 KB 7.57 KB NODE_PROD
react-dom.profiling.min.js +1.2% +1.4% 121.1 KB 122.61 KB 37.98 KB 38.51 KB NODE_PROFILING
ReactDOM-dev.js +1.1% +1.1% 971.01 KB 981.94 KB 215.97 KB 218.25 KB FB_WWW_DEV
ReactDOM-prod.js 🔺+1.3% 🔺+1.5% 405.32 KB 410.57 KB 73.6 KB 74.68 KB FB_WWW_PROD
ReactDOMTesting-dev.js +1.2% +1.1% 908.33 KB 919.25 KB 202.83 KB 205.13 KB FB_WWW_DEV
ReactDOMTesting-prod.js 🔺+1.3% 🔺+1.5% 386.38 KB 391.57 KB 70.34 KB 71.38 KB FB_WWW_PROD
react-dom-server.browser.development.js +0.3% +0.4% 128.46 KB 128.87 KB 34.39 KB 34.52 KB NODE_DEV
ReactDOMTesting-profiling.js +1.3% +1.5% 386.38 KB 391.57 KB 70.34 KB 71.38 KB FB_WWW_PROFILING
react-dom-server.browser.production.min.js 🔺+0.3% 🔺+0.2% 20.02 KB 20.07 KB 7.4 KB 7.42 KB NODE_PROD
react-dom-unstable-fizz.browser.production.min.js 0.0% -0.1% 1.17 KB 1.17 KB 690 B 689 B UMD_PROD
ReactDOMServer-dev.js +0.3% +0.4% 138.6 KB 139.01 KB 35.33 KB 35.48 KB FB_WWW_DEV
react-dom-test-utils.development.js 0.0% -0.0% 49.61 KB 49.61 KB 13.52 KB 13.52 KB NODE_DEV
ReactDOMServer-prod.js 🔺+0.3% 🔺+0.3% 47.57 KB 47.71 KB 11.07 KB 11.11 KB FB_WWW_PROD
react-dom-test-utils.production.min.js 0.0% -0.0% 10.85 KB 10.85 KB 4.1 KB 4.1 KB NODE_PROD
react-dom-unstable-native-dependencies.development.js 0.0% -0.0% 58.97 KB 58.97 KB 14.71 KB 14.71 KB UMD_DEV
react-dom-unstable-fizz.browser.production.min.js 0.0% -0.2% 1013 B 1013 B 602 B 601 B NODE_PROD
react-dom-unstable-native-dependencies.production.min.js 0.0% -0.1% 10.31 KB 10.31 KB 3.49 KB 3.48 KB UMD_PROD

react-native-renderer

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
ReactNativeRenderer-dev.js +1.7% +1.6% 638.61 KB 649.46 KB 138.35 KB 140.63 KB RN_OSS_DEV
ReactNativeRenderer-prod.js 🔺+2.0% 🔺+2.3% 261.74 KB 266.95 KB 45.41 KB 46.44 KB RN_OSS_PROD
ReactNativeRenderer-profiling.js +1.9% +2.2% 273.54 KB 278.75 KB 47.66 KB 48.71 KB RN_OSS_PROFILING
ReactFabric-dev.js +1.7% +1.7% 620.56 KB 631.32 KB 134.02 KB 136.28 KB RN_OSS_DEV
ReactFabric-prod.js 🔺+2.1% 🔺+2.4% 253.71 KB 258.94 KB 43.92 KB 44.96 KB RN_OSS_PROD
ReactFabric-profiling.js +2.0% +2.2% 265.51 KB 270.73 KB 46.19 KB 47.2 KB RN_OSS_PROFILING

react-art

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-art.development.js +2.0% +2.0% 525.92 KB 536.47 KB 113.35 KB 115.58 KB NODE_DEV
react-art.production.min.js 🔺+2.2% 🔺+2.5% 70.02 KB 71.53 KB 20.99 KB 21.51 KB NODE_PROD
ReactART-dev.js +1.8% +1.8% 587.24 KB 598.04 KB 122.8 KB 125.07 KB FB_WWW_DEV
ReactART-prod.js 🔺+2.2% 🔺+2.5% 236.83 KB 242.07 KB 40.19 KB 41.19 KB FB_WWW_PROD
react-art.development.js +1.8% +1.7% 621.26 KB 632.3 KB 131.02 KB 133.25 KB UMD_DEV
react-art.production.min.js 🔺+1.4% 🔺+1.6% 104.98 KB 106.48 KB 31.82 KB 32.32 KB UMD_PROD

react-test-renderer

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-test-renderer-shallow.development.js 0.0% -0.0% 38.39 KB 38.39 KB 9.29 KB 9.29 KB UMD_DEV
react-test-renderer.development.js +2.0% +2.0% 532.48 KB 543.04 KB 114.84 KB 117.08 KB NODE_DEV
react-test-renderer.production.min.js 🔺+2.1% 🔺+2.5% 71.81 KB 73.32 KB 21.52 KB 22.06 KB NODE_PROD
ReactTestRenderer-dev.js +1.9% +1.9% 559.51 KB 570.3 KB 118.04 KB 120.32 KB FB_WWW_DEV
react-test-renderer.development.js +2.0% +2.0% 558.74 KB 569.78 KB 116.2 KB 118.46 KB UMD_DEV
react-test-renderer.production.min.js 🔺+2.1% 🔺+2.3% 71.99 KB 73.5 KB 21.89 KB 22.39 KB UMD_PROD

react-reconciler

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-reconciler.development.js +2.1% +2.0% 562.07 KB 573.77 KB 118.83 KB 121.19 KB NODE_DEV
react-reconciler.production.min.js 🔺+2.3% 🔺+2.5% 74.47 KB 76.19 KB 21.99 KB 22.55 KB NODE_PROD
react-reconciler-reflection.production.min.js 0.0% -0.1% 2.8 KB 2.8 KB 1.2 KB 1.2 KB NODE_PROD

react

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react.development.js 0.0% -0.0% 100.31 KB 100.31 KB 24.65 KB 24.64 KB UMD_DEV
react.production.min.js 0.0% -0.0% 11.59 KB 11.59 KB 4.56 KB 4.56 KB UMD_PROD
react.profiling.min.js 0.0% -0.0% 15.13 KB 15.13 KB 5.68 KB 5.67 KB UMD_PROFILING
react.development.js 0.0% -0.0% 62.14 KB 62.14 KB 16.92 KB 16.92 KB NODE_DEV
react.production.min.js 0.0% -0.1% 6.53 KB 6.53 KB 2.65 KB 2.65 KB NODE_PROD
React-dev.js +0.8% +0.6% 75.66 KB 76.26 KB 19.16 KB 19.28 KB FB_WWW_DEV
React-prod.js 🔺+2.0% 🔺+1.9% 17.72 KB 18.08 KB 4.58 KB 4.67 KB FB_WWW_PROD
React-profiling.js +2.0% +1.9% 17.72 KB 18.08 KB 4.58 KB 4.67 KB FB_WWW_PROFILING

react-debug-tools

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-debug-tools.development.js +4.6% +2.7% 18.48 KB 19.33 KB 5.17 KB 5.31 KB NODE_DEV
react-debug-tools.production.min.js 🔺+5.9% 🔺+4.4% 5.49 KB 5.81 KB 2.1 KB 2.2 KB NODE_PROD

ReactDOM: size: 🔺+0.3%, gzip: 🔺+0.2%

React: size: 0.0%, gzip: -0.0%

Size changes (stable)

Generated by 🚫 dangerJS against dee1164

@sizebot
Copy link

sizebot commented Feb 7, 2020

Details of bundled changes.

Comparing: 30a998d...dee1164

react

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react.development.js +0.6% +0.6% 108.33 KB 108.97 KB 25.89 KB 26.03 KB UMD_DEV
react.production.min.js 🔺+1.7% 🔺+1.3% 12.45 KB 12.66 KB 4.81 KB 4.87 KB UMD_PROD
react.profiling.min.js +1.3% +0.9% 15.99 KB 16.2 KB 5.89 KB 5.95 KB UMD_PROFILING
react.development.js +0.9% +0.7% 69.79 KB 70.39 KB 18.15 KB 18.27 KB NODE_DEV
react.production.min.js 🔺+2.9% 🔺+2.2% 7.51 KB 7.73 KB 2.91 KB 2.98 KB NODE_PROD
React-dev.js +0.8% +0.7% 74.98 KB 75.59 KB 18.99 KB 19.11 KB FB_WWW_DEV
React-prod.js 🔺+2.0% 🔺+2.0% 17.66 KB 18.02 KB 4.57 KB 4.66 KB FB_WWW_PROD
React-profiling.js +2.0% +2.0% 17.66 KB 18.02 KB 4.57 KB 4.66 KB FB_WWW_PROFILING

react-dom

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-dom.profiling.min.js +1.2% +1.5% 125.48 KB 127 KB 39.11 KB 39.68 KB NODE_PROFILING
react-dom-unstable-native-dependencies.development.js 0.0% -0.0% 58.98 KB 58.98 KB 14.71 KB 14.71 KB UMD_DEV
ReactDOM-dev.js +1.2% +1.1% 925.09 KB 936.02 KB 206.08 KB 208.39 KB FB_WWW_DEV
react-dom-unstable-native-dependencies.production.min.js 0.0% -0.1% 10.32 KB 10.32 KB 3.5 KB 3.49 KB UMD_PROD
ReactDOMServer-dev.js +0.3% +0.4% 137.75 KB 138.16 KB 35.19 KB 35.34 KB FB_WWW_DEV
react-dom-test-utils.production.min.js 0.0% 0.0% 11.01 KB 11.01 KB 4.17 KB 4.17 KB UMD_PROD
ReactDOMTesting-dev.js +1.2% +1.2% 881.68 KB 892.61 KB 197.41 KB 199.71 KB FB_WWW_DEV
ReactDOMTesting-prod.js 🔺+1.4% 🔺+1.5% 372.82 KB 378.01 KB 68.14 KB 69.19 KB FB_WWW_PROD
react-dom-test-utils.development.js 0.0% -0.0% 49.63 KB 49.63 KB 13.53 KB 13.53 KB NODE_DEV
ReactDOMTesting-profiling.js +1.4% +1.5% 372.82 KB 378.01 KB 68.14 KB 69.19 KB FB_WWW_PROFILING
react-dom-server.node.development.js +0.3% +0.4% 131.19 KB 131.6 KB 34.86 KB 34.99 KB NODE_DEV
react-dom-test-utils.production.min.js 0.0% 0.0% 10.86 KB 10.86 KB 4.1 KB 4.11 KB NODE_PROD
react-dom-server.node.production.min.js 🔺+0.3% 🔺+0.2% 20.88 KB 20.94 KB 7.63 KB 7.65 KB NODE_PROD
react-dom.development.js +1.2% +1.1% 922.96 KB 934.14 KB 201.97 KB 204.24 KB UMD_DEV
react-dom-server.browser.development.js +0.3% +0.4% 136.99 KB 137.42 KB 35.02 KB 35.17 KB UMD_DEV
react-dom.production.min.js 🔺+1.2% 🔺+1.5% 121.36 KB 122.87 KB 38.67 KB 39.23 KB UMD_PROD
react-dom-server.browser.production.min.js 🔺+0.3% 🔺+0.2% 20.56 KB 20.62 KB 7.52 KB 7.54 KB UMD_PROD
react-dom.profiling.min.js +1.2% +1.4% 125.19 KB 126.7 KB 39.91 KB 40.49 KB UMD_PROFILING
react-dom.development.js +1.2% +1.1% 878.54 KB 889.23 KB 199.45 KB 201.69 KB NODE_DEV
react-dom-server.browser.development.js +0.3% +0.4% 129.97 KB 130.38 KB 34.61 KB 34.74 KB NODE_DEV
react-dom.production.min.js 🔺+1.2% 🔺+1.5% 121.51 KB 123.02 KB 37.87 KB 38.42 KB NODE_PROD
react-dom-server.browser.production.min.js 🔺+0.3% 🔺+0.3% 20.48 KB 20.53 KB 7.48 KB 7.5 KB NODE_PROD
ReactDOM-prod.js 🔺+1.4% 🔺+1.5% 378.71 KB 383.96 KB 68.83 KB 69.9 KB FB_WWW_PROD
ReactDOMServer-prod.js 🔺+0.3% 🔺+0.4% 46.86 KB 47.01 KB 10.89 KB 10.93 KB FB_WWW_PROD
ReactDOM-profiling.js +1.4% +1.6% 395.97 KB 401.55 KB 71.81 KB 72.92 KB FB_WWW_PROFILING
react-dom-unstable-native-dependencies.development.js 0.0% -0.0% 55.94 KB 55.94 KB 14.48 KB 14.48 KB NODE_DEV
react-dom-unstable-native-dependencies.production.min.js 0.0% -0.1% 10.05 KB 10.05 KB 3.38 KB 3.37 KB NODE_PROD
react-dom-unstable-fizz.browser.production.min.js 0.0% -0.2% 1 KB 1 KB 610 B 609 B NODE_PROD

react-native-renderer

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
ReactFabric-prod.js 🔺+2.1% 🔺+2.4% 253.72 KB 258.95 KB 43.93 KB 44.97 KB RN_OSS_PROD
ReactFabric-profiling.js +2.0% +2.2% 265.52 KB 270.75 KB 46.2 KB 47.21 KB RN_OSS_PROFILING
ReactNativeRenderer-prod.js 🔺+2.0% 🔺+2.3% 261.76 KB 266.96 KB 45.42 KB 46.45 KB RN_OSS_PROD
ReactNativeRenderer-profiling.js +1.9% +2.2% 273.55 KB 278.76 KB 47.67 KB 48.72 KB RN_OSS_PROFILING
ReactNativeRenderer-dev.js +1.7% +1.6% 641.33 KB 652.18 KB 138.7 KB 140.98 KB RN_FB_DEV
ReactNativeRenderer-prod.js 🔺+2.0% 🔺+2.3% 261.9 KB 267.11 KB 45.45 KB 46.48 KB RN_FB_PROD
ReactNativeRenderer-profiling.js +1.9% +2.2% 273.7 KB 278.9 KB 47.7 KB 48.74 KB RN_FB_PROFILING
ReactFabric-dev.js +1.7% +1.7% 620.58 KB 631.33 KB 134.03 KB 136.28 KB RN_OSS_DEV
ReactFabric-dev.js +1.7% +1.7% 623.29 KB 634.04 KB 134.35 KB 136.61 KB RN_FB_DEV
ReactFabric-prod.js 🔺+2.1% 🔺+2.4% 253.87 KB 259.1 KB 43.96 KB 45 KB RN_FB_PROD
ReactNativeRenderer-dev.js +1.7% +1.6% 638.63 KB 649.47 KB 138.36 KB 140.64 KB RN_OSS_DEV
ReactFabric-profiling.js +2.0% +2.2% 265.67 KB 270.89 KB 46.23 KB 47.24 KB RN_FB_PROFILING

react-art

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-art.development.js +1.7% +1.7% 642.22 KB 653.26 KB 134.71 KB 136.93 KB UMD_DEV
react-art.production.min.js 🔺+1.4% 🔺+1.6% 107.73 KB 109.24 KB 32.57 KB 33.09 KB UMD_PROD
react-art.development.js +1.9% +1.9% 546.03 KB 556.6 KB 117.08 KB 119.32 KB NODE_DEV
react-art.production.min.js 🔺+2.1% 🔺+2.5% 72.71 KB 74.23 KB 21.7 KB 22.24 KB NODE_PROD
ReactART-dev.js +1.9% +1.9% 559.17 KB 569.97 KB 117.15 KB 119.42 KB FB_WWW_DEV
ReactART-prod.js 🔺+2.3% 🔺+2.7% 227.98 KB 233.22 KB 38.76 KB 39.8 KB FB_WWW_PROD

react-test-renderer

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-test-renderer-shallow.development.js 0.0% -0.0% 38.4 KB 38.4 KB 9.3 KB 9.3 KB UMD_DEV
react-test-renderer-shallow.production.min.js 0.0% 0.0% 12.92 KB 12.92 KB 3.94 KB 3.94 KB UMD_PROD
ReactTestRenderer-dev.js +1.9% +1.9% 559.52 KB 570.31 KB 118.05 KB 120.32 KB FB_WWW_DEV
react-test-renderer.development.js +2.0% +2.0% 558.76 KB 569.8 KB 116.21 KB 118.48 KB UMD_DEV
react-test-renderer.production.min.js 🔺+2.1% 🔺+2.3% 72.02 KB 73.53 KB 21.91 KB 22.41 KB UMD_PROD
react-test-renderer.development.js +2.0% +2.0% 532.5 KB 543.06 KB 114.85 KB 117.09 KB NODE_DEV
react-test-renderer.production.min.js 🔺+2.1% 🔺+2.5% 71.83 KB 73.34 KB 21.54 KB 22.07 KB NODE_PROD

react-reconciler

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-reconciler.development.js +2.0% +1.9% 584.51 KB 596.22 KB 123.03 KB 125.42 KB NODE_DEV
react-reconciler.production.min.js 🔺+2.2% 🔺+2.4% 77.57 KB 79.28 KB 22.75 KB 23.3 KB NODE_PROD
react-reconciler-reflection.production.min.js 0.0% -0.1% 2.81 KB 2.81 KB 1.21 KB 1.21 KB NODE_PROD

react-debug-tools

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-debug-tools.development.js +4.6% +2.7% 18.49 KB 19.34 KB 5.18 KB 5.32 KB NODE_DEV
react-debug-tools.production.min.js 🔺+5.8% 🔺+4.3% 5.5 KB 5.82 KB 2.11 KB 2.21 KB NODE_PROD

ReactDOM: size: 0.0%, gzip: -0.1%

React: size: 🔺+1.7%, gzip: 🔺+1.3%

Size changes (experimental)

Generated by 🚫 dangerJS against dee1164

@bvaughn bvaughn force-pushed the useMutableSource branch 2 times, most recently from 0f7cf73 to 560acda Compare February 7, 2020 22:50
@bvaughn bvaughn marked this pull request as ready for review February 13, 2020 22:06
fiber: Fiber,
expirationTime: ExpirationTime,
) {
function scheduleUpdateOnFiber(fiber: Fiber, expirationTime: ExpirationTime) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This export was never referenced (only the scheduleWork alias below) so I removed it.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mind you removing scheduleWork instead? 😆 I meant to rename all the callsites to the more specific name during the last refactor.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure.

@bvaughn
Copy link
Contributor Author

bvaughn commented Feb 13, 2020

This PR is ready for review. I've updated it to reflect the latest thinking about the API.

@bvaughn bvaughn changed the title useMutableSource hook [work in progress] useMutableSource hook Feb 13, 2020
@bvaughn
Copy link
Contributor Author

bvaughn commented Feb 13, 2020

I do not understand the Circle CI lint_build error. I'm tempted to say it's a CI problem and not related to this PR though.


isSafeToReadFromSource =
pendingExpirationTime === NoWork ||
pendingExpirationTime >= expirationTime;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to self: If this component is newly mounting, we need to always check the version number as well, to account for the case of tearing between newly mounted components that have not yet subscribed to the store (and so may miss a tear).

I'll add a test for this (and update the code) tomorrow.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related: if the source version matches the work-in-progress version, then you don't need to check if the current render is inclusive of the pending mutation updates. Because that check would only fail if there was a mutation since the work-in-progress version was originally set, in which case the the version would have been bumped.

In other words, you only have to check for pending mutation updates if there's not already a work-in-progress version, i.e. if this is the first mutable read of this source during this render. For subsequent reads, the version alone is sufficient to tell if there was a mutation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this sounds right. I could avoid checking the expiration time if I already have a WIP version. Good suggestion.

Copy link
Collaborator

@acdlite acdlite left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work! (Sorry for the avalanche of comments! Because the high level approach is essentially correct, my feedback is more detailed than usual for a first review pass.)

Some of my feedback is a bit nitpicky, but I really just have one big note: we should fork the mount and update implementations. They have different trade offs. (By "mount" I mean both new useMutableSource hooks and when the source or config changes, i.e. "mount" in the useEffect sense of the word.)

For renders that don't include a new hook, we can support full concurrency with no de-opts.

To illustrate what I mean, here is an additional test case to consider — multiple pending mutations at different priorities, no new mounts:

  • Render a tree that includes multiple useMutableSource hooks.
  • Mutate one or more of the sources at high priority.
  • Before processing the update, mutate again at a lower priority.
  • Now flush the work.
  • There should be two commits: the high priority mutation, followed by the low priority mutation.

The way you would implement this is with a state queue. You can use the one provided by useState/useReducer. This works because for mutations after mount, we can call getSnapshot in the event that triggers the mutation instead of in the render phase. Then we add that snapshot to the queue. At that point, they're exactly like normal updates. If the source is mutated before the update commits, that's fine because we already took the snapshot. If there are multiple snapshots, that's also fine because each one of them is in the queue.

It's only in the mount case where we're forced to read during the render phase, which necessitates the extra consistency checks.

packages/react-reconciler/src/ReactFiberHooks.js Outdated Show resolved Hide resolved
packages/shared/ReactMutableSource.js Outdated Show resolved Hide resolved
fiber: Fiber,
expirationTime: ExpirationTime,
) {
function scheduleUpdateOnFiber(fiber: Fiber, expirationTime: ExpirationTime) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mind you removing scheduleWork instead? 😆 I meant to rename all the callsites to the more specific name during the last refactor.

packages/shared/ReactMutableSource.js Outdated Show resolved Hide resolved
packages/react-reconciler/src/ReactFiberWorkLoop.js Outdated Show resolved Hide resolved
packages/react-reconciler/src/ReactFiberHooks.js Outdated Show resolved Hide resolved
packages/react-reconciler/src/ReactFiberCommitWork.js Outdated Show resolved Hide resolved

hook.memoizedState = memoizedState;

if (prevSource !== source) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the config changes?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting question. My thinking had been that a change in config would really just be a change in getSnapshot dependencies, and not a change in subscribe. Sebastian's suggestion on the RFC to get rid of the wrapper config object and pass both callbacks directly would make this more explicit, in which case I will add a check for that here as well.

isPrimaryRenderer: boolean,
): void {
if (isPrimaryRenderer) {
mutableSource._workInProgressVersionPrimary = version;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the context implementation, we fire a warning if we detect multiple primary renderers.

if (__DEV__) {
if (
context._currentRenderer !== undefined &&
context._currentRenderer !== null &&
context._currentRenderer !== rendererSigil
) {
console.error(
'Detected multiple renderers concurrently rendering the ' +
'same context provider. This is currently unsupported.',
);
}
context._currentRenderer = rendererSigil;

We should probably do the same here. Although I think it will need to go in getWorkInProgressVersion instead of set.

);

// If an update is already scheduled for this source, re-use the same priority.
if (alreadyScheduledExpirationTime !== undefined) {
Copy link
Collaborator

@acdlite acdlite Feb 14, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check means that if you update at one priority, then again at a different priority, the first priority always wins. We should support multiple concurrent priorities, which is solved by using the useState queue. (The test case I described in my other comment would cover this.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, my thinking on this was that scheduling more than one update is only valid if we eagerly read snapshot values. I think the key question is whether or not that's a positive change. If it is, then going with an update queue makes sense.

@acdlite
Copy link
Collaborator

acdlite commented Feb 14, 2020

The lint build error is legit. I looked at the ReactFabric-dev artifact and found this:

// ReactFabric-dev.js:11656
useMutableSource: function(source, config) {
  currentHookNameInDev = "useMutableSource";
  mountHookTypesDev();
  return Snapshot > config;
}

Looks like some sort of build error related to Flow types?

Phew that we have the lint build task! Also an argument for why we should be running more of our tests against the bundles (and therefore moving away from .internal test files).

@bvaughn
Copy link
Contributor Author

bvaughn commented Feb 14, 2020

The lint build error is legit.

That's interesting! 😄 I initially assumed it was a transient issue yesterday. Just hadn't had the chance to dig into it yet today.

This was referenced Nov 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed React Core Team Opened by a member of the React Core Team
Projects
None yet
Development

Successfully merging this pull request may close these issues.