-
Notifications
You must be signed in to change notification settings - Fork 47.2k
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
Feature request: useContextGetter #21329
Comments
Thanks for those links! I read through them and there are definitely some interesting ideas. I think in particularly Reading through #16956 I also find some mention of a request for a hook along the lines of |
You can do something like this in your code: function createContext(defaultValue) {
const Ctx = React.createContext(defaultValue);
const GetterCtx = React.createContext(() => defaultValue);
const BaseProvider = Ctx.Provider;
function Provider({ value, children }) {
const ref = React.useRef(value);
React.useLayoutEffect(() => {
ref.current = value;
});
const getter = React.useCallback(() => ref.current, []);
return <BaseProvider value={value}>
<GetterCtx.Provider value={getter}>
{children}
</GetterCtx.Provider>
</BaseProvider>;
}
Ctx.Provider = Provider;
Ctx.GetterCtx = GetterCtx;
return Ctx;
}
function useContextGetter(ctx) {
return React.useContext(ctx.GetterCtx);
} |
Thanks @vkurchatkin I like this approach. Here's a quick demo with your code: https://stackblitz.com/edit/react-contextgetter |
@vkurchatkin That's pretty handy and I think would work for most cases, however, reading through this comment about If this were a part of React, I would hope that there would be some way for the context value to be updated earlier in the lifecycle than |
This issue has been automatically marked as stale. If this issue is still affecting you, please leave any comment (for example, "bump"), and we'll keep it open. We are sorry that we haven't been able to prioritize it yet. If you have any new additional information, please include it with your comment! |
This is still relevant! |
This issue has been automatically marked as stale. If this issue is still affecting you, please leave any comment (for example, "bump"), and we'll keep it open. We are sorry that we haven't been able to prioritize it yet. If you have any new additional information, please include it with your comment! |
Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please create a new issue with up-to-date information. Thank you! |
Right now the only hook for consuming a React context is
useContext
, which is great for most cases. However, one downside is that it results in a component re-rendering whether or not the context itself is directly used for displaying something. Take the following example:Here the value of
MyContext
is only used whenonClick
is called, it is not used by any returned DOM elements or child components. However, if the value ofmyContext
changes,ExpensiveComponent
will re-render despite no differences in what is being displayed.One way to prevent this component from over re-rendering would be to provide a hook along the lines of
useContextGetter
. It would preventExpensiveComponent
from re-rendering by returning a getter function forMyContext
that would allowonClick
to lazily access the current context's value. This getter would be a stable function similar to the callbackuseState
returns.Here's the above example rewritten to use
useContextGetter
:There is some prior art for an API similar to this with Recoil's
useRecoilCallback
making it possible to access Recoil state inside of a callback without requiring a component to re-render when the state changes. One could also construct similar functionality with React Redux'suseStore
and callinggetState()
on the store inside of a callback.The above examples I used are pretty trivial and one could simply refactor the part that uses
MyContext
into a separate child component to avoid re-renderingExpensiveComponent
. However, its not difficult to imagine a scenario where such a refactor may be challenging or a component being used in enough places that the re-render causes performance degradation.The text was updated successfully, but these errors were encountered: