React Sharing Global State
Table of Contents + −
In the last lesson, React Context Provider Component, you wrapped your app in a Provider and passed it a value. Now let’s build real global state: one piece of state in context that any component can read and change while everyone stays in sync.
🤔 What is global state?
Let’s define it clearly, because the word “global” can sound bigger than it is.
- Global state is state that many components across the app share, not just one component’s local state.
- It lives in one place, the provider, instead of being copied into each component.
- Any component inside the provider can read it, and any can update it.
- When one component changes it, every component reading it updates too.
So global state is just shared state with a single home. Context is how React gives many components access to that one home.
🧩 The full pattern in one place
Let’s build a simple example: a login status shared across the whole app. We’ll create the context, hold the state in the provider, and use it from two different components.
First, the context and a provider component that holds the state.
import { createContext, useState } from "react";
export const AuthContext = createContext(null);
export function AuthProvider({ children }) { const [user, setUser] = useState(null); // the shared state lives here
// share both the user and functions to change it const value = { user, setUser };
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;}Walk through this provider component.
AuthContextis the context object, created once.AuthProvideris a component that holds the actualuserstate withuseState.- It bundles
userandsetUserintovalueand passes it to the Provider. {children}means whatever you wrap insideAuthProviderbecomes the tree that can read this.
children is just what you wrap
The children prop is everything you put between <AuthProvider> and </AuthProvider>. So the provider can wrap any part of your app, and all of it gets access to the shared state.
🌳 Wrap the app in the provider
Now we wrap the app so every component inside can reach the shared user state.
import { AuthProvider } from "./AuthContext";
function App() { return ( <AuthProvider> <Navbar /> <LoginButton /> </AuthProvider> );}So both Navbar and LoginButton are inside AuthProvider. That means both can read and change the user, even though they’re separate components.
📖 Read the state in one component
The Navbar reads the shared user to show who’s logged in. It pulls the value out with useContext.
import { useContext } from "react";import { AuthContext } from "./AuthContext";
function Navbar() { const { user } = useContext(AuthContext); // read the shared user
return <nav>{user ? `Hello, ${user.name}` : "Not logged in"}</nav>;}See how reading works.
useContext(AuthContext)gives back thevalueobject from the provider.- We pull out
userfrom it. - If there’s a user, we greet them; if not, we show “Not logged in”.
✏️ Update the state from another component
The LoginButton, somewhere else entirely, changes the same shared state. It uses setUser from the context.
import { useContext } from "react";import { AuthContext } from "./AuthContext";
function LoginButton() { const { user, setUser } = useContext(AuthContext); // read and the updater
if (user) { return <button onClick={() => setUser(null)}>Log out</button>; } return <button onClick={() => setUser({ name: "Alex" })}>Log in</button>;}Here’s the magic of global state.
LoginButtonreadssetUserfrom the same context.- Clicking “Log in” calls
setUser({ name: "Alex" }), which changes the shared state. - Because
Navbarreads the same context, it instantly updates to “Hello, Alex”. - One component changed the state, and a separate component reflected it, with no props passed between them.
Output
(at first)Navbar: Not logged in Button: Log in
(after clicking Log in)Navbar: Hello, Alex Button: Log out🔄 Why everyone stays in sync
It’s worth seeing why this works, so you trust it.
- The state lives in one place, the provider, not copied around.
- Every reader uses
useContextto get that same value. - When
setUserchanges the state, the provider re-renders with the new value. - So every reader gets the new value and updates together. That single source of truth is what makes it “global”.
⚠️ Common Mistakes
A common mistake is keeping separate copies of the state in different components instead of reading from one context.
// ❌ each component has its own user state - they don't stay in syncfunction Navbar() { const [user, setUser] = useState(null); // a separate, unrelated copy}
// ✅ read the one shared user from contextfunction Navbar() { const { user } = useContext(AuthContext); // the single shared state}Keep these in mind.
- Don’t duplicate the state in each component. Keep one copy in the provider and read it.
- Don’t forget to share the updater (
setUser) in the value, or components can read but never change. - Don’t put fast-changing values here; global state suits shared data that changes occasionally.
✅ Best Practices
A few habits for global state.
- Keep the state in one provider component, and share both the value and its updaters.
- Read it with
useContextwherever you need it; never copy it into local state. - Use a custom provider component (like
AuthProvider) to hold the state and logic neatly. - Reserve context for genuinely shared data, like auth, theme, or cart; keep local things local.
This scales to real apps
This exact pattern, state in a provider plus useContext to read and update, is how real apps share things like the logged-in user or a shopping cart. Once you’ve built one, you can build any of them.
🧩 What You’ve Learned
- ✅ Global state is shared state with one home, readable and updatable by many components
- ✅ Hold the state with
useStateinside a provider component, and share it through the Provider’svalue - ✅ Share both the value and its updater functions in the
valueobject - ✅ Read and update it from any component inside with
useContext - ✅ When one component updates the state, every reader re-renders and stays in sync
- ✅ Keep one source of truth; don’t copy the state into each component
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
What is global state?
Why: Global state lives in one place, the provider, and many components across the app can read and change it, all staying in sync.
- 2
Where should the actual state live in this pattern?
Why: Keep one copy of the state in the provider with useState, and share it via the value. Components read it instead of holding their own copies.
- 3
How does a far-away component update the shared state?
Why: Share the updater in the value object. Any component reads it with useContext and calls it, which changes the one shared state.
- 4
Why do all readers stay in sync when the state changes?
Why: The state lives once in the provider. When it changes, the provider re-renders and every component reading the context gets the new value.
🚀 What’s Next?
Now you can build real shared global state. Next you’ll build a theme switcher, a light/dark toggle shared across the whole app with context.