An open API service indexing awesome lists of open source software.

https://github.com/chantastic/reactcontext.com

a gentle intro to the what, where, when, why, and how of React Context
https://github.com/chantastic/reactcontext.com

Last synced: about 1 year ago
JSON representation

a gentle intro to the what, where, when, why, and how of React Context

Awesome Lists containing this project

README

          

# React Context

*Updated for React 19.*

## TLDR on React Context

Use Context for *implicit*, client-only, data distribution.
Context is an alternative props (which are *explicit* and available to both server and client components).

Context allows for component relationships similar to HTML elements `

  • ` and `
      `.
      Here, data applied to the parent, has an implicit impact on how children render.

      ```jsx title="an HTML comparison"


      1. Tenth

      2. Eleventh

      3. Twelfth


      ```

      Context makes a value available to all descendents.

      ```jsx title="parent providing context"


      ```

      Those descendents must opt in to Context.

      ```jsx title="child component using context"
      function UserAvatar() {
      let user = React.use(UserContext);

      return ;
      }
      ```

      Unlike HTML, Context does not require direct parent-child relationships.
      Contexts can be sent and recieved *"thru"* intermediate elements, components, and context providers.

      ```jsx title="child composing two contexts"





      ```

      This doc is a guide for implementing Context in React.






      ## Table of contents

      ## Real-world React Context (a "shit" example)

      *Shit* is fine word in my house. But my mom hates it.
      So I tell my kids to use another word when around grandma.

      Here's how I'd implement that with React Context.

      ```jsx
      // It's ok to say "shit" as a default expletive
      let ExpletiveContext = React.createContext("shit");

      // But context is important. Learn to account for it.
      function ContextualExclamation() {
      let word = React.use(ExpletiveContext);

      return Oh {word}!;
      }

      // When at Grandma's house, say "snap" instead
      function AtGrandmasHouse() {
      return (



      )
      );

      // => Oh snap!
      ```

      ## Context is a 3-part system: create, use, and provide

      Context is a 3-part system:
      **create**, **use**, **provide**.

      **Create** context with `React.createContext`.

      ```jsx title="create context"
      let NameContext = React.createContext("Guest");
      ```

      **Use** context with `React.use`.

      ```jsx title="use context"
      function ContextualGreeting() {
      let name = React.use(NameContext);

      return

      👋 {name}!

      ;
      }
      ```

      **Provide** context by rendering the `Context` with a `value` prop.

      ```jsx title="provide context value"

      // =>

      👋 Chan!


      ```

      ## Context is an alternative to props that is implicit

      Context is most useful when many components require the same data.

      Here's an example:

      ```jsx title="app.jsx"
      <>



      >
      ```

      These components may, in turn, also pass the same data to their children.

      ```jsx title="user_avatar.jsx" {9}
      function UserRelatedPosts({ user }) {
      const related_posts = getRelatedPosts(user);

      return (


        {related_posts.map(post => (

      • {post.body}


      • ))}

      );
      }
      ```

      With Context, the data is set once on the parent.

      ```jsx title="app.jsx" ins=/(UserContext.*)>/ del=/ user={user}/



      ```

      And descendent components opt into this data with `use`.

      ```jsx title="user_avatar.jsx" ins=/const user =.+/ del=/{ user }/
      function UserAvatar({ user }) {
      const user = React.use(UserContext);

      return ;
      }
      ```

      ## Think of props like wired and Context like wireless (a mental model)

      **Props are like wires.**
      They "connect" data between components.
      Like wires, the components have to be "touching".
      Meaning that components *holding* data have to render components that *need* it.

      **Context is like a wireless.**
      It sends a "signal" that is received by children.
      Like wireless, components don't need to be "touching" they only need to be "in range".
      Meaning that children of context can *recieve* the signal that context sends.

      ## Context is available to all descendents (indifferent to how deeply nested)

      Every descendent/child of a Context provider can observe Context's value.

      ```jsx title="any descendent can recieve context" {4, 7, 10}
      function App() {
      return (

      {/* UserContext can be received here… */}


      {/* also here… */}


      {/* and anywhere in this tree… */}



      );
      }
      ```

      ## Context is composable

      Contexts can be chidren of other Contexts. Order doesn't matter much unless it matters to your app.

      ```jsx title="two contexts; one component"
      let OrgContext = React.createContext();
      let PersonContext = React.createContext();

      function App() {
      return (





      );
      }

      function UserOrgBizCard() {
      const org_name = React.use(OrgContext);
      const person_name = React.use(PersonContext);

      return (


      {person_name}, {org_name}

      );
      }
      ```

      ## Context can not be sent "over the wire" (it's not available to the server)

      Context is only available to client components.
      Server components cannot recieve context.

      ## Context is used to implement the compound components pattern

      The "compound component" pattern describes components that are isolated but interdependent.
      In HTML, `li` and `ol` are interdepedent. As are `option` and `select`.

      This same interdependent relationship can be implemented using React Context.

      ```jsx title="app.jsx"
      import * as React from "react";
      import * as User from "./user";

      function App() {
      const [editing, setEditing] = React.useState(false);

      const [user, setUser] = React.useState({
      name: "Guest",
      avatar_url: "https://example.com/avatar.png"
      });

      return (

      {isEditing
      ? {
      setUser({ name: formData.get('name') });
      setEditing(false);
      }}
      />
      : <>


      setEditing(true)}>
      Edit

      >
      }

      );
      }
      ```

      ```jsx title="user.jsx"
      import * as React from "react";

      export const Context = React.createContext();

      function Avatar() {
      const user = React.use(UserContext);

      return
      }

      function Name() {
      const user = React.use(UserContext);

      return (
      {user.name}
      );
      }

      function Form({ action }) {
      const user = React.use(UserContext);

      return (




    );
    }
    ```

    ## Context can be used to implement distributed state management (with useReducer)

    Context makes it possible to distribute data to every component in a component tree.

    It's used to distribute data, not manage state.
    That said, it provides the mechanism needed to both distribute state and dispatch updates.

    Here's an minimum connection of the two.

    ```jsx title="app.jsx"
    import * as React from 'react';
    import * as ClickCount from './click_count';

    function App() {
    const clickState = React.useReducer(
    ClickCount.reducer,
    ClickCount.initialState
    );

    return (








    );
    }
    ```

    ```jsx title="click_count.jsx"
    import * as React from 'react';

    export const initialState = { count: 0 };

    export function reducer(state, action) {
    switch (action.type) {
    case 'increment':
    return { count: state.count + 1 };
    default:
    return state;
    }
    }

    export const Context = React.createContext();

    export function Show() {
    let [state] = React.use(Context);

    return <>{state.count}>;
    }

    export function IncrementAction() {
    let [, dispatch] = React.use(Context);

    return (
    dispatch({ type: 'increment' })}>
    Increment count

    );
    }
    ```

    ## Any JavaScript value can be be shared via Context

    Context can take any shape.
    Here are examples of valid Contexts values, using a default `value`:

    ```jsx
    let StringContext = React.createContext("string");

    let NumberContext = React.createContext(42);

    let FunctionContext = React.createContext(() =>
    alert("Context function")
    );

    let ArrayContext = React.createContext([
    "some",
    "array",
    "elements"
    ]);

    let ObjectContext = React.createContext({
    aString: "string",
    aNumber: 42,
    aFunction: () => alert("Context function"),
    anArray: ["some", "array", "elements"]
    });

    let MapAndSetContext = React.createContext(
    new Map([
    [
    'Taylor Swift',
    new Set(['Tortured Poets Department', 'Midnights', 'Evermore']),
    ],
    ])
    );
    ```

    `value` can be complex structures like React Elements, class components, and function components.

    ```jsx
    let ReactElementContext = React.createContext(
    React Element
    );

    let FunctionalComponentContext = React.createContext(
    props => Function Component
    );
    ```

    ## You can set a default `value` when creating Context

    Context can be initialized with a default `value`.
    When a component attempts to `use` Context, but no corresponding `Context` is found, a default value will be used.

    ```jsx
    let UserContext = React.createContext("Guest");

    function UserGreeting () {
    let name = React.use(UserContext);

    return Hi {name}!;
    }

    let App = props => (



    {/* => Hi Guest! */}



    {/* => Hi Bulbasaur! */}


    );
    ```

    ## Context can be used inline with the render prop pattern

    Context exposes a `Consumer` component for inline context use. This allows context to be consumed without creating new components.

    ```jsx


    {value => {value}}

    ```

    Here's an example where multiple contexts are created and used.

    ```jsx
    const OrgContext = React.createContext();
    const PersonContext = React.createContext();

    function App () {
    return (



    {organization => (

    {person => (

    {person}, {organization}

    )}

    )}



    )
    }

    // => Yakko, ACME Co.
    ```

    ## Context can cascade

    Consumers use the value from the nearest `Context`.
    Where none is present, the `createContext` default value is used.

    ```jsx
    const MyContext = React.createContext("default");

    function ShowContextValue() {
    const value = React.use(MyContext);

    return <>{value}>;
    }

    function App() {
    return (
    <>

    {/* "outer" */}


    {/* "inner" */}

    {/* "default" */}
    >
    );
    }
    ```

    ## What is Legacy Context?

    Legacy Context refers to a set of APIs that were removed in React 19.
    These include class-based component context.

    Read the [Legacy Context doc](https://reactjs.org/docs/legacy-context.html) for more details.

    © 2024 Michael Chan, Some Rights Reserved

    Creative Commons License
    This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.