DEV Community

adelinealmanzar
adelinealmanzar

Posted on

React Context vs. Containment

Context allows us to pass data through the component tree without relying on props every time (often referred to as ‘broadcasting’ data). Implementing context is useful for when we need many components at different nesting levels to have access to the same data (ex: UI theme, locale preference, current user) or in other words, when we need data to be considered ‘global’.

Implementing Context

The following code breakdown is inspired from the UI theme use-case example provided by the React documentation.

To use and update context, first we would need to define our theme object from which we will be pulling the context values from.

const themes = {
  light: {
    foreground: "#000000",
    background: "#eeeeee"
  },
  dark: {
    foreground: "#ffffff",
    background: "#222222"
  }
};
Enter fullscreen mode Exit fullscreen mode

Then we need to use React.createContext to create a context for theme with a default value. In the code example below, we’re using the themes.dark object as our default value.

const ThemeContext = React.createContext(themes.dark)
Enter fullscreen mode Exit fullscreen mode

We then need to use .Provider to provide an updated value for the specific context.

function App() {
    return (
        //provide the theme context's value to change/update the previous theme context's value
        <ThemeContext.Provider value={themes.light}>
            <Navigation />
        </ThemeContext.Provider>
    )
}
Enter fullscreen mode Exit fullscreen mode

To access our theme context value, we can use the useContext(SpecifiedContext) hook. useContext(ThemeContext) accepts our ThemeContext context variable in the format of an object (the returned object value from React.createContext) and it returns the current context value for the specific context. We can save this returned context value to a variable in order to use it in our code.

function ThemedButton() {
    // save theme context value to theme variable
    const theme = useContext(ThemeContext)
    return (
        // use theme variable to manipulate button styling
        <button style={{ background: theme.background, color: theme.foreground }} >
            My styling comes from theme context!!!
        </button>
    )

function Navigation() {
    // return our ThemedButton component to display our ThemeButton
    return <ThemedButton />
}
Enter fullscreen mode Exit fullscreen mode

The current context value is determined by the value prop of the nearest SpecifiedContext.Provider above the calling component in the tree. When the nearest SpecifiedContext.Provider above the component updates, the useContext hook will trigger a re-render with the updated latest context value.

Implementing Containment

Something to consider is that context makes component reuse harder. So, if we need to simply avoid the prop code redundancy of passing props through unnecessary intermediate levels, we should consider using the children prop within component composition. Component composition is React’s original development model of using either explicitly defined props or using implicit children props to pass down JSX components or elements via containment.

children is a special prop that passes implicit children elements directly into a component’s output via nesting within the parent component.

function ChildComponent(props) {
    return (
        <div>
            {props.children}
        </div>
    )
}

function ParentComponent() {
    return (
        <ChildComponent>
            <h1>I am the first child prop!</h1>
            <h2>I am the second child prop!</h2>
        </ChildComponent>
    )
}
Enter fullscreen mode Exit fullscreen mode

We can make our own custom convention if we’re needing to use a more specific children-prop-breakdown.

function ChildComponent(props) {
    return (
        <div>
            <div>{props.left}</div>
            <div>{props.right}</div>
        </div>
    )
}

function ParentComponent() {
    return (
        <ChildComponent>
            left={<LeftChildComponent />}
            right={<RightChildComponent />}
        </ChildComponent>
    )
}
Enter fullscreen mode Exit fullscreen mode

Containment is useful because we can pass down entire components or JSX elements down as props instead of passing down props through intermediate components.

To read more on composition, feel free to refer to the React documentation on the topic.

TLDR:

Containment can be used for most use cases when we simply need to decouple a child from its immediate parent (so that we don’t have so much redundancy in passing down props within intermediate relationships). Usually, this means that children and parent components have a singular nesting pattern.

Context, on the other hand, is useful for ‘broadcasting’ our data - accessing data by many components at different nesting levels.

Resources

Top comments (0)