DEV Community

Cover image for React Hooks: useContext()
Aden Eilers
Aden Eilers

Posted on

React Hooks: useContext()

What are React hooks?
They are functions in React that allow you to add react functionality to function based components.

What is the useContext hook?
This hook is used to efficiently pass state down the component chain. Prop drilling has historically been used to do this. Prop drilling is the process of passing state values as props deep down a component chain. Often times, this results in passing props to components that do not need them. The useContext hook allows us to pass state values down the component chain without prop drilling.

The best applications for this hook are to pass global values to children components. A few example use cases are application theme switching, and logged in user information (user id, user name...). This hook is not recommended as a replacement for the state management library Redux. Currently it is not robust enough to handle many of Redux's use cases. This hook can work great for small applications that may only have a few globally used pieces of state.
The syntax follows a common pattern:

import { createContext, useContext } from "react";

//Initialize our context. 
//null is the default value if none are later provided.
//Often this is done in a different file.
const UserContext = createContext(null)

const ParentComponent = () => {
  //We wrap our children component in the Provider 
  return (
    <UserContext.Provider value={'Aden'}>
      <ChildComponent />
    </UserContext.Provider>
  );
};

const ChildComponent = () => {

  //Access the context passed down from the ParentComponent
  const user = useContext(UserContext)
  return (
    <>
      <h1>Child Component</h1>
      <p>{user}</p>
    </>     
  );
};

export default ParentComponent;
Enter fullscreen mode Exit fullscreen mode

Here is an example using the useEffect and useState hooks:

import { useState, useEffect, createContext, useContext } from "react";

const UserContext = createContext(null)

const App = () => {
  const [users, setUsers] = useState([]);

  useEffect(() => {
    const fetchData = async () => {

      //Fetches a list of ten users
      const response = await fetch(
        "https://jsonplaceholder.typicode.com/users"
      );

      // If the HTTP status code is 200-299
      if (response.ok) {
        const json = await response.json();
        setUsers(json);
      }
    };

    fetchData();
  }, []);

  return (
    <UserContext.Provider value={users}>
      <ChildComponent1 />
    </UserContext.Provider>
  );
};

const ChildComponent1 = () => {

  //This component does not need the users data
  return (
    <>
      <h1>ChildComponent1</h1>
      <ChildComponent2 />
    </>

  )
}

const ChildComponent2 = () => {

  const users = useContext(UserContext)

  return (
    <div>
      <ul>
        {/*If the data exists, display a list of 
        users names*/}
        {users &&
          users.map((user) => {
            return <li key={user.id}>{user.name}</li>;
          })}
      </ul>
    </div>
  )
}

export default App;
Enter fullscreen mode Exit fullscreen mode

More information about useContext can be found in the React docs: https://reactjs.org/docs/context.html

Leave comment if you have any questions or feedback.

Top comments (8)

Collapse
 
peerreynders profile image
peerreynders

This hook is used to manage state globally in your application.

No, it's not a state manager.

Quote:

...context is ready to be used for low frequency unlikely updates (like locale/theme)... It's not ready to be used as a replacement for all Flux-like state propagation.

At best it's a mechanism used for dependency injection of globally shared values. So it's a good place to keep a reference to a handle that allows you to subscribe to an actual state manager where the state changes do not propagate through context.

The change detection in context allows components to "catch" when their provider becomes available. Values exposed via context are expected to change extremely infrequently if at all.

Why React Context is Not a "State Management" Tool (and Why It Doesn't Replace Redux)

Collapse
 
fig781 profile image
Aden Eilers

Very good point! I made edits to the post to reflect this. Thank you for the article. I read it and learned a lot.

Collapse
 
air_choosy profile image
priolo

But what is the alternative? This is a hotly debated issue!
As for me, I use the context as a STORE in many projects.
If I have to optimize a branch of the VIEW I use "useMemo" on the components.
The result is an absolutely acceptable speed and above all a very clean and maintainable code.

Collapse
 
peerreynders profile image
peerreynders

From the article already linked to:

There's a lot of posts out there that recommend setting up multiple separate contexts for different chunks of state, both to cut down on unnecessary re-renders and to scope concerns. Some of those also suggest adding your own "context selector components", which require a mixture of React.memo(), useMemo(), and carefully splitting things up so there's two separate contexts for each segment of state (one for the data, and one for the updater functions). Sure, it's possible to write code that way, but at that point you're just reinventing React-Redux, poorly.

So using context in this fashion is outside of it's intended use of sharing data which changes infrequently. People seem to use context for state management because "it seems to work well enough" - lets hope "until it doesn't" never comes.

Kent C. Dodds escalation past using "just context" is to use Jotai.

The creator of Jotai, Daishi Kato, published the blog post When I Use Valtio and When I Use Jotai where he differentiates between

  • data-centric apps which use Zustand or Valtio
  • component-centric apps which use Jotai

This aligns with Mark Erikson's earlier observation:

Michel Westrate, creator of Mobx advocates for app-centric design in UI as an Afterthought.

Depending on the type of application perhaps an app-centric design could be more appropriate than the apparent default of component-centric design.

Using context for DIY state management may be symptom of needing to switch to an app-centric design style.

Thread Thread
 
air_choosy profile image
priolo • Edited

there are a lot of quotes of all kinds on the internet.
As far as I'm concerned:
I've seen a lot of "component-centric" applications and it's just a pattern you should never use
Dividing the LOGIC from the VIEW is not "optional" (for medium-large software) especially for SPA applications

Thread Thread
 
air_choosy profile image
priolo

@peerreynders after so many days I can tell you. YOU ARE RIGHT!!!
But I give you an alternative! Which is blatant with React18.

reactjs.org/docs/hooks-reference.h...

import React, { useSyncExternalStore } from "react";
import { createRoot } from "react-dom/client";

// create EXTERNAL STORE
const myStore = {
    state: {
        value: ""
    },
    callback: null,
    subscribe: (callback) => {
        myStore.callback = callback
        // unsubscribe
        return () => myStore.callback = null
    },
    getSnapshot: () => myStore.state,
    changeState: (newState) => {
        myStore.state = newState
        myStore.callback()
    }
}

// use STORE in VIEW
function App() {

    const currentState = useSyncExternalStore(
        myStore.subscribe,
        myStore.getSnapshot,
    )

    const handleClick = e => myStore.changeState({value: currentState.value + "!"})

    // render
    return (<div>
        <input 
            value={currentState.value} 
            // call MUTATOR. NOTE: you must pass ONLY the "payload"
            onChange={(e)=>myStore.changeState({value:e.target.value})} 
        />
        <button onClick={handleClick}>add !</button>
    </div>);
}

// React 18
const root = createRoot(document.getElementById('root'))
root.render(<React.StrictMode><App /></React.StrictMode>)
Enter fullscreen mode Exit fullscreen mode
Collapse
 
andrematias profile image
André Matias

That is nice.
In some cases I don't want use Redux...
Thanks for the tip.

Collapse
 
air_choosy profile image
priolo

I use this basic method.
I made a library to make things easier:
JON