-
Notifications
You must be signed in to change notification settings - Fork 75
How to use useMemo to optimize context consumers #673
Comments
The dependency of useMemo here means that it will always return a new version when count changes and trigger a rerender. Both The useMemo here means that if for some reason the Another thing to keep in mind is that useMemo doesn't guarantee that it won't recalculate its values. |
Yes, that makes sense @felipeleusin. |
@felipeleusin @giossa94 thanks for your responses, I made an example in codesandbox and I see it now! I think the concept is actually quite tricky so I think I might write more about this later. Here's the codesandbox I created: https://codesandbox.io/s/xj5p774ywz
I thought this would be true! But I made a specific hook called just, EDIT: Ah I think I understand, |
I'm not sure and it's early here but if you want to solve this with a hook you need to create a second context that only has the setCount as its value. Since this doesn't change, the components using it don't need to rerender. That being said, this is most likely a premature optmization. You would end up with some code like:
and having an useSetCount like:
|
Hi guys! I was about to ask the same question, correct me if I'm wrong but I don't see the reason for using |
@patrykkarny , I'm working on a blog post about this topic actually. I'll share it with you as soon as I'm done! It's a bit tricky to see why Without |
Glad to hear you're making a blog post about it @terrencewwong! I was going to do that today, but now I can write about something else 👍 😄 |
hey @terrencewwong, thank you for the codesandbox examples, the topic is quite tricky and interesting, I can't wait to read your blog post about that :D in the meantime I played a little with the examples and it is true that without
const MemoizedIncrement = () => {
const [_, setCount] = useCount()
return React.useMemo(() => {
console.log(' MemoizedIncrement')
return (
<div>
<button onClick={() => setCount(c => c + 1)}>+1 (memoized)</button>
</div>
)
}, [setCount])
} |
Look forward to a blog post explaining why useMemo is required here. I use a little helper function to help create a store and contexts documented here and would be interested to find out if useMemo could help optimize. ✌️ |
Thanks to @kentcdodds for directing me here! The sandbox from @terrencewwong was useful in validating what I was curious about. Specifically, I wanted to know if we could revise the example from Kent's blog post to remove the call to original
revised
Unfortunately, based on what I'm seeing from the sandbox this will not work. Even if state does not change across renders for |
Hey y'all! I've been reading your examples and comments (thanks for sharing what you're learned) but I'm still a bit confused about this. In this Codesandbox, my App component has a // App.js
import { CountProvider, useCount } from "./count-context";
function DisplayCount() {
console.log('Rendering DisplayCount');
const { count } = useCount();
return <div>DisplayCount: {count}</div>;
}
function NoContextComponent() {
console.log('Rendering: NoContextComponent');
return <div>I do not have a context</div>;
}
function IncreaseCount() {
const { increment } = useCount();
return (
<div>
<button onClick={() => increment()}>Incremenet Count</button>
</div>
);
}
function App() {
const [toggle, setToggle] = React.useState(false);
return (
<div className="App">
<h1>Context + UseMemo</h1>
<NoContextComponent />
<IncreaseCount />
<DisplayCount />
<button onClick={() => setToggle(!toggle)}>
Toggle {toggle ? 'Off' : 'On'}
</button>
</div>
);
} // count-context.js
const CountContext = React.createContext();
const CountProvider = props => {
const [count, setCount] = React.useState(0);
const value = React.useMemo(() => {
return { count, setCount };
}, [count]);
return <CountContext.Provider value={value} {...props} />;
};
function useCount() {
const context = React.useContext(CountContext);
if (!context) {
throw new Error("useCount must be used within a CountProvider");
}
const { count, setCount } = context;
const increment = () => setCount(c => c + 1);
return {
count,
increment
};
}
export { CountProvider, useCount }; React Profiler ResultsWhen the React's profiler - Toggle button clicked.This happens when I click the `Toggle` button, everything gets rendered, except the `CountProvider` which I think it's expected First click - Toggle buttonSecond click - Toggle buttonWhen the |
Hi @alejandronanez !
I think this is expected behavior, since you're calling I hope this helps. |
Hello!
I read your blog article recently on Application State Management with React
There was something in the article I didn't understand. What is the call to
React.useMemo
doing in theCountProvider
?I assumed this would be for preventing rerenders of context consumers, i.e. components that use the custom
useCount
hook. But I checked out the codesandbox for the article and noticed that bothCountDisplay
andCounter
always rerender when thecount
changes. IsuseMemo
required for a different case that's not yet present in the example?NOTE
Here's the original PR that started this discussion (cc @giossa94): kentcdodds/kentcdodds.com#142
The text was updated successfully, but these errors were encountered: