ReactJS - Understanding useContext hook in Reactjs with example

ReactJS - Understanding useContext hook in Reactjs with example

In a typical React application, data is passed top-down (from parent to child) by props, however this can be challenging for some prop types (such locale preference and UI theme), which are needed by numerous components that are nested at different levels within an application.

Without having to manually provide props down via each nested component, context offers a means to pass data or state through the component tree. For a tree of React components, it is intended to share information that can be regarded as global information, such as the currently authenticated user or theme (e.g. color, paddings, margins, font-sizes).

Context is used by Context API. Context and Provider Consumer Components transmit the data, however writing the lengthy functional code to leverage this Context API is quite time-consuming. Therefore, using the useContext hook eliminates the requirement to use the Consumer Component and makes the code easier to read and less verbose. React 16.8 has a new hook called useContext.

Problem with useState hook in ReactJS

The topmost parent component in the stack that needs access to the state should hold it.

We have numerous nested components as an example. The stack's top and bottom components both require access to the state.

Without Context, we must transmit the state as "props" via each nested component in order to accomplish this. Prop drilling is the term for this.

Example:

import { useState } from "react"; const ParentComponent = () => { const [data, setData] = useState("Another Techs"); const [user, setUser] = useState("Alex"); return ( <div> <h1> Hello, {user} </h1> <ChildComponent1 data={data} /> </div> ); }; const ChildComponent2 = ({data}) => { return ( <div> <ChildComponent3 data={data}> </div> ) } const ChildComponent3 = ({data}) => { return ( <div> <ChildComponent4 data={data}> </div> ) } const ChildComponent4 = ({data}) => { return ( <div> <ChildComponent5 data={data}> </div> ) } const ChildComponent5 = ({data}) => { return ( <div> <h2> Welcome to {data} !! </h2> </div> ) }

Despite the fact that components 2-4 did not require the state, they had to send it on so that component 5 could receive it.

Context to the rescue

The context, the provider extrapolated from the context, and the consumer are the three players required to apply the React context.

Syntax:

const contextName = useContext(initialValue);

The value that React provides is accepted by useContext. When its value changes, you must first createContext and then re-render the component, although memorising can still improve performance.

The example application would seem as follows when the context is applied to it:

import { useState,createContext,useContext } from "react"; const DataContext = createContext(); const ParentComponent = () => { const [data, setData] = useState("Another Techs"); const [user, setUser] = useState("Alex"); return ( <div> <UserContext.Provider value={data}> <h1> Hello, {user} </h1> <ChildComponent1 /> </UserContext.Provider> </div> ); }; const ChildComponent2 = () => { return ( <div> <ChildComponent3 > </div> ) } const ChildComponent3 = () => { return ( <div> <ChildComponent4 > </div> ) } const ChildComponent4 = () => { return ( <div> <ChildComponent5 > </div> ) } const ChildComponent5 = () => { const data = useContext(DataContext); return ( <div> <h2> Welcome to {data} !! </h2> </div> ) }

Let's look into more detail what has been done.

  • First, const DataContext = createContext() creates the context that's going to hold the data.

  • Second, inside the <ParentComponent /> component, the application's child components are wrapped inside the user context provider: <UserContext.Provider value={data}>.

Note that the value prop of the provider component is important: this is how you set the value of the context.

  • Finally, <ChildComponent5 /> becomes the consumer of the context by using the built-in useContext(DataContext) hook. The hook is called with the context as an argument and returns the user name value.

  • <ChildComponent2/> to <ChildComponent4/> components don't have to pass down the data prop. That is the great benefit of the context: it removes the burden of passing down data through the intermediate components.

What happens if context value is changed ?

All of the context provider's consumers are alerted and re-rendered when the context value is updated by changing the context provider's value prop (<Context.Provider value=value />).

For example, if I change the user data from 'Another Techs' to 'ReactJS Context Hook', then

consumer immediately re-renders to display the latest context value:

import { useState,useEffect,createContext,useContext } from "react"; const DataContext = createContext(); const ParentComponent = () => { const [data, setData] = useState("Another Techs"); const [user, setUser] = useState("Alex"); useEffect(()=>{ setTimeout(()=>{ setData("ReactJS Context Hook"); },2000); },[]); return ( <div> <UserContext.Provider value={data}> <h1> Hello, {user} </h1> <ChildComponent1 /> </UserContext.Provider> </div> ); }; const ChildComponent2 = () => { return ( <div> <ChildComponent3 > </div> ) } const ChildComponent3 = () => { return ( <div> <ChildComponent4 > </div> ) } const ChildComponent4 = () => { return ( <div> <ChildComponent5 > </div> ) } const ChildComponent5 = () => { const data = useContext(DataContext); return ( <div> <h2> Welcome to {data} !! </h2> </div> ) }

On the screen, "Another Techs" (context value) would be visible. The context value switches to "ReactJS Context Hook" after two seconds, and the screen updates to reflect the new value.

When to use useContext hook ?

Utilizing the context is primarily intended to give your components access to certain global data and to re-render when that global data is modified. When you have to pass down props from parents to kids, context solves the props drilling problem.

Holding within the context:

  • global state
  • a theme
  • an application's settings
  • an authenticated user name
  • a user's settings
  • their preferred language
  • a group of services.

On the other hand, you ought to give the decision to use context in your application significant thought.

Integrating the context hook firstly increases complexity. Complexity is increased by creating the context, encapsulating everything in the provider, and using useContext() in each consumer.

Second, introducing context hook makes unit testing the components more challenging. You would need to encase the consumer components in a context provider for unit testing. Including the elements that the context indirectly influences — the forerunners of context consumers!

Conclusion

No matter where in the components tree a child component is located, React's concept of useContext hook enables you to supply it with global data. The context must be created, provided, and consumed before it can be used.

Remember that the useContext hook adds a significant degree of complexity when incorporating it into your application. It's not always a big deal to drill the props through two or three layers of the hierarchy.

Another Techs


© 2022 Another Techs. All rights reserved.