we help companies reach their financial and branding goals. Udgama is a values-driven technology agency dedicated.

Technology

React: Advanced Hooks

In react, hooks play an important role. Hooks help us to reduce code and perform tedious operations as well as keep code clean. It makes our work easy. There are different types of hooks to react eg. useStateuseEffect, useReducer, useRef, and useContext.

We will see how to work with these hooks by creating a simple Todo application to React.

Let’s start with our application. First, we need to create a react application using “npx”. Once we have installed node and npm type the following commands in your terminal.

$ npx create-react-app react-hooks
$ cd react-hooks

Now, we will get the boilerplate code for our react project. Now open the react-hooks folder in your favorite editor and replace the following code in App.js file.

import React, { useState, useReducer } from 'react';
import "./App.css"
function appReducer(state, action) {
switch(action.type){
case 'ADD': {
return [
...state,
{
id: Date.now(),
text: 'My text' + Date.now(),
completed: false,
}
]
}
default:
return [state];
}
}
function App() {
const [state, dispatch] = useReducer(appReducer, []);
return (
<div className="App">
<button onClick={() => dispatch({ type: 'ADD'})}>ADD TODO</button>
<h1>Todos app</h1>
{
state.map(item => (
<div key={item.id}>{item.text}</div>
))
}
</div>
);
}
export default App;

In the above code, wehave implemented useReducer hook. For those familiar with redux, we will create reducer functions and initial state then we will pass this reducer function to store and the store will provide the state and dispatch function to all the children components which are under the root.

In the above code, all the tasks are handled by useReducer hook. The useReducer hook takes reducer as an argument and creates a redux store for us. We have created a new reducer, when we dispatch an action it will add a new todo.

Run the code in the terminal using the following command.

$ npm start

You will get the following output.

We can add our todo. Rather than writing the whole code in the single function, we will divide our application into components. We will write a separate function for the todo list. Replace the code after <h1> tag from the app function and add the following line.

&lt;TodoList items={state}/&gt;

Add one function in the App.js file to display a todo list.

function TodoList({ items }){
return items.map(item => (
<div key={item.id}>{item.text}</div>
));
}

To properly structure our application, we will create a new component for the todo item.

Add the following code after the TodoList function.

function TodoItem({ item }){
return (
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "center",
}}
>
{item.text}
</div>
);
}

Replace the TodoList function with the following code.

function TodoList({ items }){
return items.map(item =>
<TodoItem key={item.id} {...item} />
);
}

If you run the above code you will get the result as follow.

Till now we have added a todo and have a checkbox and delete button. But our checkbox and the Delete button is not working yet. To perform the delete functionality we have to pass the dispatch function to our parent component and so on. But we will not pass the dispatch function like this, for that, we have another hook that we’ll use to make available dispatch function for our all children name as useContext. The useContext hook gives us ability to pass value and make it available for all the children.

First, we need to create a context and provide the values in the context as follows.

const Context = React.createContext();
function App() {
const [state, dispatch] = useReducer(appReducer, []);
return (
<Context.Provider value={dispatch}>
<div className="App">
<h1>Todos app</h1>
<button onClick={() => dispatch({ type: 'ADD'})}>ADD TODO</button>
<TodoList items={state}/>
</div>
</Context.Provider>
);
}

In the above code, we have created the context using the createContext method of React and pass this context to the whole application using <Context.Provider value={dispatch}> wrapper and we have provided a dispatch function as one of the context values.

Now, we can use the dispatch function in each child component using useContext hook. Lets see how to use useContext hook to get dispatch function in other children’s components.

First, import the useContext hook from the React as follows.

import { useContext } from 'react';

We have created a context using the createContext method of React. We will use this context to get the dispatch function in any function or children component. We are calling dispatch function in the TodoItem component where we have our delete button and checkbox. Let’s see how to use useContext hook here.

Replace the following code with the previous code of the TodoItem component.

function TodoItem({ id, text, completed }){
const dispatch = useContext(Context);
return (
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "center",
margin: "10px 10px",
}}
>
<input type="checkbox" checked={completed} onChange={() => dispatch({ type: 'COMPLETED', payload: id })}/>
<input style={{ margin: "0 10px"}} type="text" defaultValue={text}/>
<button onClick={() => dispatch({ type: 'DELETE', payload: id})}>Delete</button>
</div>
);
}

To perform delete and checked operation we need two more cases in our reducer function. One is for delete and the other is for checked todo. We will add two cases in our appReducer function.

Add the following code in the appReducer function.

case 'DELETE': {
return state.filter(item => item.id !== action.payload);
}
case 'COMPLETED': {
return state.map(item => {
if (item.id === action.payload){
return {
...item,
completed: true
};
}else {
return item;
}
});
}

Now, save your code and run the application. You can add the todo, delete and check the checkbox of every todo item. But when we refresh the page we are not able to see the previously added todo list.

What should we do now? Don’t worry we have the solution for that. We will use the useEffect hook which is provided by React. We will store all the todos in localStorage when we create a new todo it will get added in our todo list and when we reload our page we can see all the list of our todo.

Let’s see how to use the useEffect hook to store the todo list and fetch it.

Add the following code in App function.

useEffect(() => {
console.log("inside first");
const data = localStorage.getItem('todos');
dispatch({ type: 'RESET', payload: JSON.parse(data)});
}, []);
useEffect(() => {
console.log("inside second");
localStorage.setItem('todos', JSON.stringify(state));
}, [state]);

If you look at the above code we have used two useEffect hooks. In your React application, you can use multiple useEffect hooks. This useEffect hook works like a lifecycles method of React. If you want to perform any operation before your component gets mounted or unmounted, you can perform all such operations in useEffect hook.

If you run the above code and see the output we can see the previously added todo list when we reload our component as follows.

The output of the application to perform add, delete and check the checkbox functionality

The above code we have used useEffect hook and in first useEffect hook, we have to pass an empty array as a dependency. It means that this useEffect hook will execute once when we load our component. So we are getting our values of previous todos only once when we load the component. But, if we want to fetch the value every time when we update our todo, then we will need to use the useRef hook in our React application. The useRef hook gives users the ability to create an immutable object which will store the value in the current variable. Now, let’s see how to use the useRef hook.

Import useRef hook from React. Add the following line of code at the top.

import { useRef } from "react";

Add the following line in the main App function.

const didRun = useRef(<strong>false</strong>);

Replace the first useEffect in the App component with the following code of useEffect hook.

useEffect(() => {
if (!didRun.current){
const data = localStorage.getItem('todos');
dispatch({ type: 'RESET', payload: JSON.parse(data)});
didRun.current = true;
}
console.log("FDSFASDF");
});

Now, we are left with one task. We are able to add and delete our todo item. Now we will perform Edit todo item functionality. For that, we will use another hook called useState. This hook is used to store a state and gives a function to change that state. Again we have to add a new case in the appReducer function. I have updated the code for the above operation. You will get the full code of our todo app with React hooks from the following snippet.

Full code of Todo App to React with Hooks.

import React, { useState, useEffect, useReducer, useContext, useRef } from 'react';
import "./App.css"
function appReducer(state, action) {
    switch(action.type){
        case 'ADD': {
            return [
                ...state,
            {
                id: Date.now(),
                text: "My text" + Date.now(),
                completed: false,
            }
            ]
        }
        case 'CHANGETEXT': {
            return state.map(item => {
                if(item.id === action.payload.ID){
                    return {
                        ...item,
                        text: action.payload.text
                    };
                }else {
                    return item;
                }
            });
        }
        case 'DELETE': {
            return state.filter(item => item.id !== action.payload);
        }
        case 'COMPLETED': {
            return state.map(item => {
                if (item.id === action.payload){
                    return {
                        ...item,
                        completed: true
                    };
                }else {
                    return item;
                }
            });
        }
        case 'RESET': {
            return action.payload
        }
        default:
            return [state];
    }
}

const Context = React.createContext();

function App() {
    const [state, dispatch] = useReducer(appReducer, []);

    const didRun = useRef(false);

    useEffect(() => {
        if (!didRun.current){
            const data = localStorage.getItem('todos');
            dispatch({ type: 'RESET', payload: JSON.parse(data)});
            didRun.current = true;
        }
        console.log("FDSFASDF");
    });

    useEffect(() => {
        localStorage.setItem('todos', JSON.stringify(state));
    }, [state]);

    return (
        <Context.Provider value={dispatch}>
            <div className="App">
                <h1>Todos app</h1>
                <button onClick={() => dispatch({ type: 'ADD'})}>ADD TODO</button>
                <TodoList items={state}/>
            </div>
        </Context.Provider>
    );
}

export default App;

function TodoList({ items }){
    return items.map(item => 
        <TodoItem key={item.id} {...item} />
    );
}

function TodoItem({ id, text, completed }){
    const dispatch = useContext(Context);
    const [mytext, setText] = useState(null);

    return (
        <div 
            style={{ 
                display: "flex",
                flexDirection: "row",
                justifyContent: "center",
                margin: "10px 10px",
            }}
        >
            <input type="checkbox" checked={completed} onChange={() => dispatch({ type: 'COMPLETED', payload: id })}/>
            <input style={{ margin: "0 10px"}} type="text" defaultValue={text} onChange={(e) => setText(e.target.value)}/>
            <button onClick={() => dispatch({ type: 'CHANGETEXT', payload: { ID: id, text: mytext }}) }>ChangeText</button>
            <button onClick={() => dispatch({ type: 'DELETE', payload: id})}>Delete</button>
        </div>
    );
}

I have added some styles to my application. You can add it if you want and The output will look as follows.

These are some of the hooks that can be implemented in your application to increase the speed, efficiency, and reduce the code and keep your code neat and clean.

I hope this gives you a good start to take your first step into advanced hooks!