Understanding React Hooks - UseContext

Understanding React Hooks - UseContext

State management in react has been progressive from one solution to a new and improved one, all aimed at increasing accessibility of state and reducing processes of passing state between components in any part of the application tree. The process of passing state from the parent components down to child components through props is quite useful to an extent till the component in need of the state is 3 to 4 layers in the tree below the parent passing the state, to achieve this will now require passing the props at every layer till it gets to the needed child component, this process is called props drilling, which explains the process of drilling from parent component with the props down the tree to get to the needed child component.

Let’s see an example of this problem:

import { useState } from "react"

const Nav = (props) => {
  return (
    <div>
    <p>Click to login</p>
    <button onClick={props.setAuthenticated}>Login</button>
  </div>
  )
}

const Welcome = (props) => {
    return (
        <div>
            <h3>Welcome {props.user}</h3>
            <p>You're logged in</p>
        </div>
    )
}

const ChildComponent = (props) => {

  return (
    <div>
      <Nav setAuthenticated={props.setAuthenticated} />
      <p>This is a child component </p>
      {props.authenticated && <Welcome user={props.user}/> }
    </div>
  );
};

const ParentComponent = () => {
  const user = "Fred";
  const [authenticated, setAuthenticated] = useState(false);
  return (
    <div>
      <p> Welcome to our site</p>
      <ChildComponent user={user}
      setAuthenticated={() => setAuthenticated(true)}
      authenticated={authenticated}/>
    </div>
  );
};

In the example above the user data was passed down through all the components as props to the actual component where it is used. This method which was quite inefficient for developers of react necessitated the need for a solution to it which brought about the UseContext hook.

UseContext hook is used to create a common data (or global state) that can be accessed throughout the component hierarchy without passing the props down manually to each level. Let's write the application to effect the useContext hook:
We first create the global states to be shared using the createContext function. Then we update the parent component to create providers which are wrappers to the child components which are consumers of the context.

const UserContext = createContext();
const AuthContext = createContext({
  auth: false,
  setAuthenticated: () => {},
});

Then editing the Parent Component:

const ParentComponent = () => {
  const [auth, setAuthenticated] = useState(false);
  return (
    <div>
      <p> Welcome to our site</p>
      <AuthContext.Provider value={{auth, setAuthenticated}}>
        <UserContext.Provider value={"Fred"}>
          <ChildComponent />
        </UserContext.Provider>
      </AuthContext.Provider>
    </div>
  );
};

The provider accepts a value prop, the state passed down to the context consumers. An observation from the above code, multiple providers can be wrapped around a consumer component with different values and can be accessed using separate useContext calls to the particular context needed.

Updating the consumer components to use context.

const ChildComponent = () => {
  const {auth} = useContext(AuthContext);
  return (
    <div>
      <Login />
      <p>This is a child component </p>
      {auth && <Welcome />}
    </div>
  );
};

const Login = () => {
  const {setAuthenticated} = useContext(AuthContext);
  const LoginAction = (value) => setAuthenticated(value)
  return (
    <div>
      <p>Click to login</p>
      <button onClick={() => LoginAction(true)}>Login</button>
    </div>
  );
};

const Welcome = () => {
  const user = useContext(UserContext);
  return (
    <div>
      <h3>Welcome {user}</h3>
      <p>You're logged in</p>
    </div>
  )};

In the code above, we accessed the state using useContext which takes in the actual context being consumed. Destructuring was necessary since the state was an object to get the particular value required.

So that's a wrap, we have been able to avoid prop drilling and provide the necessary state to the necessary component using the useContext hook no matter the depth in the component tree.