Replies: 4 comments 4 replies
-
Context renders all consumers throughout the system on every change. In a larger scale app everything, the entire component tree, will render on every keystroke when you update an input field for instance. And there is little you can do about it, you can't easily bail out of updates and you can't tie components to specific state. Unless you split the context state up, which leads to even worse problems. At this point larger scale apps will eventually just fail because they have been made rigid and implicit. Assuming you have a state model function Dogs() {
// This component will *only* render when the amount of dogs changes
const dogs = useStore(state => state.dogs)
return <div>{dogs}</div>
} This article is a great summary: https://formidable.com/blog/2021/stores-no-context-api
This is the official stance from the React team:
In other words, only use context for compound components where A relies on the presence of B, for instance List > ListItem. |
Beta Was this translation helpful? Give feedback.
-
Note that with extra effort, it's possible to prevent re-rendering when using the Context API. You can create a "connect" function that take a selector and component as parameter an return a memoized component: (Anyway, I will definitely use Zustand on my project 😄 ) Create the "connect" function// context-helper.tsx
import React from 'react'
const ConnectContext = <ComponentProps extends {}>() => <Selector extends (...args: any[]) => any>(
select: Selector,
WrappedComponent: (
props: ReturnType<Selector> &
ComponentProps & { children?: React.ReactNode }
) => any
): any => {
const MemoWrapper = React.memo(WrappedComponent);
return (props: ComponentProps) => {
const selectors = select();
return <MemoWrapper {...selectors} {...props} />;
};
}; Then use it in your component// MyComponent.tsx
import React from "react";
import { ConnectContext } from "./context-helpers";
import { useDemoContext } from "./DemoContext";
const Select = () => {
const { setFirst, second } = useDemoContext();
return { setFirst, second };
};
type MyComponentProps = {};
export const MyComponent = ConnectContext<MyComponentProps>()(
Select,
({ setFirst, second }) => {
console.log("MY COMPONENT RENDER");
return (
<div>
<h3>This component will not rerender even if you change "first" value</h3>
second value {second}
<button onClick={() => setFirst(Math.random() * 100)}>
Set first to random
</button>{" "}
</div>
);
}
); Here is a codesandbox demo: https://codesandbox.io/s/react-context-avoid-too-much-rendering-thxgf3 |
Beta Was this translation helpful? Give feedback.
-
I only use context for compound composition pattern. Ex: <Menu>
<Menu.Button>...</Menu.Button>
<Menu.Items>
<Menu.Item>...</Menu.Item>
<Menu.Item>...</Menu.Item>
</Menu.Items>
</Menu>
...
const MenuButton = () => ...
const MenuItems = () => ...
const MenuItem = () => ...
const Menu = () => ...
Menu.Button = MenuButton
Menu.Items = MenuItems
Menu.Item = MenuItem |
Beta Was this translation helpful? Give feedback.
-
Hi ! What would you use for the user / session / theme globa data shared among components in an app? We use Context for complex components and compound pattern in our react components library. We use zustand in our apps. We have a context / provider for globals (user, theme) but I was thinking about switching to zustand an having a store for each. Same, thinking about having a store to handle our toast notifications. What are you thinking people ? |
Beta Was this translation helpful? Give feedback.
-
In Part "Why zustand over context?" is mentioned: "Renders components only on changes".
What is meant by that in details? With context provider, it also rerenders only those components where
useContext
is used and its value has changed. Or am I missing something?Beta Was this translation helpful? Give feedback.
All reactions