Conversation
This block is getting hard to read so I moved it to a separate function. I'm about to refactor the logic that wraps around this path. Ideally this early bailout path would happen before the begin phase phase. Perhaps during reconcilation of the parent fiber's children.
The only reason we pass `updateLanes` to some begin functions is to check if we can perform an early bail out. But this is also available as `current.lanes`, so we can read it from there instead. I think the only reason we didn't do it this way originally is because components that have two phases — error and Suspense boundaries — use `workInProgress.lanes` to prevent a bail out, since during the initial render there is no `current`. But we can check the `DidCapture` flag instead, which we use elsewhere to detect the second phase.
For internal experimentation only. This implements `unstable_useContextSelector` behind a feature flag. It's based on [RFC 119](reactjs/rfcs#119) and [RFC 118](reactjs/rfcs#118) by @gnoff. Usage: ```js const context = useContextSelector(Context, c => c.selectedField); ``` The key feature is that if the selected value does not change between renders, the component will bail out of rendering its children, a la `memo`, `PureComponent`, or the `useState` bailout mechanism. (Unless some other state, props, or context was updated in the same render.) One difference from the RFC is that it does not return the selected value. It returns the full context object. This serves a few purposes: it discourages you from creating any new objects or derived values inside the selector, because it'll get thrown out regardless. Instead, all the selector will do is return a subfield. Then you can compute the derived value inside the component, and if needed, you memoize that derived value with `useMemo`. If all the selectors do is access a subfield, they're (theoretically) fast enough that we can call them during the propagation scan and bail out really early, without having to visit the component during the render phase. Another benefit is that it's API compatible with `useContext`. So we can put it behind a flag that falls back to regular `useContext`. The longer term vision is that these optimizations (in addition to other memoization checks, like `useMemo` and `useCallback`) are inserted automatically by a compiler. So you would write code like this: ```js const {a, b} = useContext(Context); const derived = computeDerived(a, b); ``` and it would get converted to something like this: ```js const {a} = useContextSelector(Context, context => context.a); const {b} = useContextSelector(Context, context => context.b); const derived = useMemo(() => computeDerived(a, b), [a, b]); ``` (Though not this exactly. Some lower level compiler output target.)
This will to make it easier to A/B test, or to revert if we abandon the
experiment. Using a selector will not change the return type of
`useContext`. Use a userspace hook to get the selected value:
```js
function useContextSelector<C, S>(Context: C, selector: C => S): S {
const context = useContext(Context, {unstable_selector: selector});
const selectedContext = selector(context);
return selectedContext;
}
```
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
Greptile OverviewGreptile SummaryThis PR implements an experimental context selectors feature that allows components to subscribe to specific fields of a context value and bail out of re-renders when only unselected fields change. Critical Issue Found:
Architecture:
Implementation Quality:
Confidence Score: 0/5
Important Files ChangedFile Analysis
|
There was a problem hiding this comment.
Additional Comments (2)
-
packages/react-reconciler/src/ReactFiberNewContext.new.js, line 358 (link)logic: calling
selector(newSelectedValue)double-applies the selector.newSelectedValueis alreadyselector(newValue)from line 356. this should comparenewSelectedValuewitholdSelectedValuedirectly. -
packages/react-reconciler/src/ReactFiberNewContext.old.js, line 358 (link)logic: calling
selector(newSelectedValue)double-applies the selector.newSelectedValueis alreadyselector(newValue)from line 356. this should comparenewSelectedValuewitholdSelectedValuedirectly.
13 files reviewed, 2 comments
* Initial plan * Add security limits for DoS and RCE prevention in React Server Components Co-authored-by: dill-lk <241706614+dill-lk@users.noreply.github.com> * Adjust security limits to be more reasonable and add comprehensive tests Co-authored-by: dill-lk <241706614+dill-lk@users.noreply.github.com> * Fix security tests to work within test environment constraints Co-authored-by: dill-lk <241706614+dill-lk@users.noreply.github.com> * Fix trailing whitespace issues Co-authored-by: dill-lk <241706614+dill-lk@users.noreply.github.com> * Add comprehensive maintainer documentation for security fixes Co-authored-by: dill-lk <241706614+dill-lk@users.noreply.github.com> * Add master documentation index for maintainers --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: dill-lk <241706614+dill-lk@users.noreply.github.com>
Mirror of facebook/react#20646
Original author: acdlite
Based on #20890
This is not a final API. It's meant for internal experimentation only. If we land this feature in our stable release channel, it will likely differ from the version presented here.
This implements
unstable_useContextSelectorbehind a feature flag. It's based on RFC 119 and RFC 118 by@gnoff.Usage:
The key feature is that if the selected value does not change between renders, the component will bail out of rendering its children, a la
memo,PureComponent, or theuseStatebailout mechanism. (Unless some other state, props, or context was updated in the same render.)One difference from the RFC is that it does not return the selected value. It returns the full context object. This serves a few purposes: it discourages you from creating any new objects or derived values inside the selector, because it'll get thrown out regardless. Instead, all the selector will do is return a subfield. Then you can compute the derived value inside the component, and if needed, you memoize that derived value with
useMemo.If all the selectors do is access a subfield, they're (theoretically) fast enough that we can call them during the propagation scan and bail out really early, without having to visit the component during the render phase.
Another benefit is that it's API compatible with
useContext. So we can put it behind a flag that falls back to regularuseContext.The longer term vision is that these optimizations (in addition to other memoization checks, like
useMemoanduseCallback) are inserted automatically by a compiler. So you would write code like this:and it would get converted to something like this:
(Though not this exactly. Some lower level compiler output target.)