2

Hi everyone I'm new to React. I was trying to create a global state using the context api. I encountered something unexpected. What I've learned is that when a context is created, it has a provider that will wrap around those components that need the data and when the value changes, the provider will rerender all the wrapped components, right?

Take a look at this:

// AuthContext.js
export const AuthContext = createContext(null);

const AuthProvider = ({ children }) => {
        const [user, setUser] = useState({
            name: null,
        });

    return (
        <AuthContext.Provider value={{ user, setUser }}>
            {children}
        </AuthContext.Provider>
    );
};
// index.js
<AuthProvider>
    <App />
</AuthProvider>
// App.js
function App() {
    console.log("[App] ran");
    return (
        <>
            <Dashboard />
            <Login />
        </>
    );
}
// Dashboard.js
function Dashboard() {
    console.log("[Dashboard] ran");
    return <div>Dashboard</div>;
}
// Login.js
function Login() {
    console.log("[Login] ran");
    const { user, setUser } = useContext(AuthContext);
    const inputNameRef = useRef();

    return (
    <div>
        <input placeholder="Enter your name..." ref={inputNameRef} />
        <button
            onClick={() => {
            setUser(inputNameRef.current.value);
            }}
        >
        Submit
        </button>
    </div>
    );
}

When the code runs for the first time the output is:

[App] ran

[Dashboard] ran

[Login] ran

and when the submit button is clicked, the setUser function will be called and a new value will be set to the AuthProvider state. The provider should rerender all of the components, and the above output should again be logged but nope, the output is just:

[Login] ran

Something that is interesting to me is that when I use useContext in the Dashboard component it works, I mean the component will be rerendered. It's related to the useContext hook I think but don't know how it works. What is going on under the hood?

Boann
  • 48,794
  • 16
  • 117
  • 146
PedroEl
  • 41
  • 3
  • Currently there is no bailout from context API (see duplicate for more concrete example), so to your question "will rerender all the wrapped components, right?" yes. – Dennis Vash May 01 '22 at 14:45

1 Answers1

2

To quote React's official documentation on Context (https://reactjs.org/docs/context.html):

All consumers that are descendants of a Provider will re-render whenever the Provider’s value prop changes. The propagation from Provider to its descendant consumers (including .contextType and useContext) is not subject to the shouldComponentUpdate method, so the consumer is updated even when an ancestor component skips an update.

You can think of "consumer" as the component whose state "consumes" the useContext hook. AKA you'll only see a re-render from the components where the hook is used. This way not ALL children inside a Context Provider will be re-rendered on a state change, only those who consume it.

bbbppp
  • 90
  • 1
  • 6