I'm relatively new to React and I'm struggling with one thing. I have an app which has many nested components. I set up a Context
for them. There's a child component (context consumer) that I want to be able to change the context with. And I don't want to trigger re-renders only in components that use this context.
However, I cannot really change the context value in the consumer. The only thing I was able to come up with is this:
import { createContext, useContext, useState } from "react";
interface ContextType {
message: string;
setMessage: (value: string) => void;
}
const Context = createContext<ContextType>({} as ContextType);
const Consumer1 = () => {
const { message, setMessage } = useContext(Context);
return (
<div>
{message}
<button onClick={() => setMessage("bar")}>Change context</button>
</div>
);
};
const Consumer2 = () => {
return <div>Consumer2</div>;
};
const App = () => {
const [message, setMessage] = useState("foo");
const value = { message, setMessage };
return (
<div>
<Context.Provider value={value}>
<Consumer1 />
<Consumer2 />
</Context.Provider>
</div>
);
};
export default App;
CodeSandbox link: here.
So, basically when I click the button in Consumer1
, it triggers a context change, but the thing is - the context value is the App
(context provider) state. That's why it works, I am basically re-rendering App
, so it can pass the newly changed value via the provider. Consumer1
reads the new updated value and all is good. However, Consumer2
also gets re-rendered here, because its parent got re-rendered.
I guess my question is simple: is there a way to trigger a context change in the consumer without re-rendering the provider (which is usually a parent for many components) - so the only components re-rendered are the ones that use the context? Or isn't that possible?
It is not possible to update a state without causing re-render.
But what you can do is to separate that logic in different provider component, rather than the App itself.
const MessageProvider = ({children}) => {
const [message, setMessage] = useState("foo");
const value = { message, setMessage };
return <Context.Provider value={value)>{children}</Context.Provider>
}
and then use it wherever you like
const App = () => (
<MessageProvider>
<Consumer1>
<Consumer2>
<SomethingThatWontRerender />
<SomethingElse />
</MessageProvider>
)
This way you will rerender only the provider and the consumers