React Sharing Global State

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.

  • AuthContext is the context object, created once.
  • AuthProvider is a component that holds the actual user state with useState.
  • It bundles user and setUser into value and passes it to the Provider.
  • {children} means whatever you wrap inside AuthProvider becomes 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 the value object from the provider.
  • We pull out user from 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.

  • LoginButton reads setUser from the same context.
  • Clicking “Log in” calls setUser({ name: "Alex" }), which changes the shared state.
  • Because Navbar reads 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 useContext to get that same value.
  • When setUser changes 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 sync
function Navbar() {
const [user, setUser] = useState(null); // a separate, unrelated copy
}
// ✅ read the one shared user from context
function 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 useContext wherever 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 useState inside a provider component, and share it through the Provider’s value
  • ✅ Share both the value and its updater functions in the value object
  • ✅ 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. 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. 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. 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. 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.

React Theme Switcher Example

Share & Connect