DEV Community

Joma
Joma

Posted on • Edited on

useContext: simplicity matters

Props drilling is a good way of learning that nested components can send and receive properties, but in terms of simplicity and readability, props drilling is really just a pain int the a.. 😊. That's why we need useContext hook!

Steps are simple:
1. Create a context

Creating a context

2. Create a provider which will send properties to child components

Provider wrapper

3. Consume these properties

Read the value


I created a parent component called Login.jsx where I handle user inputs using useReducer hook. The state I want to pass to other child component Middleware.jsx is const[isAuthenticated, setIsAuthenticated] = useState(false) so I've should also create a Provider which wraps child component providing it with isAuthenticated prop. Middleware will consume that prop and read its value whenever it changes inside parent component and render another child component, Dashboard.jsx. If the isAuthenticated true, user will see rendered Dashboard but if it's not, there will be a custom message as a warning.
If I passed a isAuthenticated to the child component, what should I do with its setter function? Setter function allows me to change a state of the observable prop so the child components know what to do based on the given value.


A bigger picture:

  1. Creating a context and its hook (hook is optional but it gives us a secure fallback if we for example forget to create a provider but still passing props)

AuthenticationContext.jsx

import { createContext, useContext } from "react";

//create a context
export const AuthenticationContext = createContext(null);


//create a custom hook to handle issues with context being null
export const useAuthenticationHook = () =>{
    const context = useContext(AuthenticationContext);
    if(!context){ throw new Error("Context is null, you should check your provider.")}
    return context;
}
Enter fullscreen mode Exit fullscreen mode
  1. Creating a provider in a parent component passing a mutable prop to a child component and handling user authentication Login.jsx
import React, { useState, useReducer } from 'react'
import { AuthenticationContext } from './AuthenticationContext';
import Middleware from './Middleware';

const Login = () => {
    //MUTABLE state
    const[isAuthenticated, setIsAuthenticated] = useState(false);
    //useReducer: initialState, dipatch, reducer | state

    const initialState = {username:'', password:''};
    function reducer (state, action){
        switch(action.type){
            case 'handleTextState':
                return {
                    ...state,
                    [action.field] : action.value

                }
        }

    }

    const[state, dispatch] = useReducer(reducer, initialState);

    function handleUserLogin(e){
        //prevent form reset
        e.preventDefault();
        //set shareable state to true if the user is correct
        /* const res = await fetch('your api endpoint for login',{
        method:'POST',
        body: JSON.stringify({state.username, state.password})
        })
        if(res.status === 200){
        setIsAuthenticated(true);
        } */

        setIsAuthenticated(true);
    }



  return (
    <div>
        <h3>To see your dashboard, please login first!</h3>
        <div>
            <form onSubmit={handleUserLogin}>
                <input placeholder='Your username...' name="username" value={state.username} type="text" onChange={(e)=> dispatch({
                    type:"handleTextState",
                    field:e.target.name,
                    value:e.target.value
                })} />

                 <input placeholder='Your password...' name="password" value={state.password} type="text" onChange={(e)=> dispatch({
                    type:"handleTextState",
                    field:e.target.name,
                    value:e.target.value
                })} />
                <button type="submit">Login</button>
            </form>
            <div>
                <AuthenticationContext.Provider value {{isAuthenticated}}>
                <Middleware />
                </AuthenticationContext.Provider>
            </div>
        </div>
    </div>
  )
}

export default Login
Enter fullscreen mode Exit fullscreen mode
  1. Using a passed prop inside a child component and reading its value

Middleware.jsx

import React from 'react'
import { useAuthenticationHook } from './AuthenticationContext'
import Dashboard from './Dashboard';

const Middleware = () => {
    const {isAuthenticated} = useAuthenticationHook();
  return (
    <div>
        {
         isAuthenticated ? <Dashboard /> : <p>You need to login first!</p>
        }
    </div>
  )
}

export default Middleware
Enter fullscreen mode Exit fullscreen mode

Top comments (0)