How to use React Hooks in Functional Components

Hooks are a new React feature for state management that came with React 16.8 in 2019. This additional API allows you to use states and lifecycle methods inside functional components. That means you no longer need to define a Class Component in order to manage changes in the state of your React application corresponding to the user input.

With using the Class Component, we can easily find ourself writing complex component using several lifecycle methods, which makes our code hard and tricky to read and maintain. Beside that, accessing state, props and methods is not that easy, we need always to use this keyword to access props and state and to access methods within the class component we need to bind them to this.

So React hooks APIs came to handle all these concerns and make developers’ life easier by allowing them to:

  • Write clearer code which is easier to maintain.
  • Write more readable code without using constructors, binding and this keyword.
  • Add states and perform side effects inside functional components .
  • Reuse existing components.
  • Move up and down the components tree with Flexibility.
  • Start using Hooks without rewriting existing code (hooks are completely opt-in).

Before you start creating and using Hooks, Keep in mind :

  1. Only use these Hooks in a functional component.
  2. Not use Hooks inside loops, conditions, or regular nested functions.
  3. Hooks can call other Hooks.
  4. The React team doesn’t plan to deprecate React class components.
  5. Not all React class lifecycle methods are possible with Hooks yet.

React Hooks provides us with several Hooks and the following 4 basic hooks are the commonly used by react developers:

  • State Hook – useState: Allows you to use local state inside React functional components, without converting them to class components.
  • Effect Hook – useEffect: This hooks replaces componentDidMount, componentDidUpdate, and componentWillUnmount used in class component.
  • Context Hook – useContext: Context hook is used within React applications that are using a React app context.
  • Reducer Hook – useReducer:  useReducer is convenient for managing complex state changes.

In this article you are going to learn and understand the basic React Hooks. We will go through each hook and explain not only how to use it but why you might want to and some advantages they have over class components. 

Prerequisites

To follow along this article make sure you:

Now you can open your project in the code editor SV code, then go to app.js and create a functional component: you can use an arrow function or regular function to use the React Hooks in side it.

//  App.js

import React from 'react';

function App() {



return (
<div>

</div> 

);

} 
export default App;

Then you can run npm start to see the changes in your browser along this article.

 $ npm start

React State Hook: useState

useState is one of the most important React Hooks. It allows us to use and manipulate local state inside React functional components, without using setState or converting them to class components.

To use useState hook you need first to import it from React.

import React, { useState } from 'react';

The useState first parameter is used to declare or define the initial actual value of the state, in the following example the initial state is 11.

useState hook return an array called array destructuring in JavaScript: the first value in the array is the value of the state and the second value is a function that’s lets you update the first value usually called setValue.

import React, { useState } from 'react';

function App() {
const [value, setValue] = useState(11);

return (
<div>
<button onClick ={()=>setValue(value+1)}>+</button> 
<div>{value}</div> 
</div> 

);

} 
export default App;

You should know that you can actually pass a function to setValue function. So instead of grabbing value from up, you can pass in an update arrow function that takes current value as a single parameter and return the new state.

This is a good practice to avoid conditions race and having two updates happening at the same time and the update get overwritten.

import React, { useState } from 'react';

function App() {
const [value, setValue] = useState(11);

return (
<div>
<button onClick ={()=>setValue(currentValue => currentValue+1)}>+</button> 
<div>{value}</div> 
</div> 

);

} 
export default App;

In case we have two values in our useState and we want to update only one of them, useState works slightly different from setState. In the following example, we want to update and increment only the first value: value1.

To do that, we have to make some changes because now we have to deal with an object :

  • The first one is to replace the value in the array with the values object and the initial state with initial state object.
  • The second one is to change the update function by adding the …state to make sure that we keep the no changed values (value2 in this example) that are inside of the object and increment and overwrite only the value1.
// Example of updating only one value using ...state

import React, { useState } from 'react';

function App() {
const [{value1, value2}, setValue] = useState({value1: 11, value2: 5});

return (
<div>
<button onClick ={()=>setValue(state =>( {...state, value1: state.value1 +1}))}>+</button> 
<div>value1: {value1}</div> 
<div>value2: {value2}</div> 
</div> 

);

} 
export default App;

Or you can do it this way:

// Second way to update only one value

import React, { useState } from 'react';

function App  () {
const [{value1, value2}, setValue] = useState({value1: 11, value2: 5});

return (
<div>
<button onClick ={()=>setValue(state =>( { value1: state.value1 +1, value2: state.value2 }))}>+</button> 
<div>value1: {value1}</div> 
<div>value2: {value2}</div> 
</div> 

);

} 
export default App;

One of the advantage of useState hook is that you can have multiple useState in your Functional Component. This allows us to break up the useState in the precedent example in order to simplify our code. So when we click on the button it will change only the value1 and it won’t change value2.

// Using multiple useState to simplify our code

import React, { useState } from 'react';

function App() {
const [value1, setValue1] = useState( 11);
const [value2, setValue2] = useState( 5);

return (
<div>
<button onClick ={()=>setValue1(value =>value + 1)}>+</button> 
<div>value1: {value1}</div> 
<div>value2: {value2}</div> 
</div> 


);

} 
export default App;

If we want change both values I have to add setValue2(value =>value + 1) to the onClick function so our Component will look like:

// Changing  both values

import React, { useState } from 'react';

function App() {
const [value1, setValue1] = useState( 11);
const [value2, setValue2] = useState( 5);

return (
<div>
<button onClick ={()=>{setValue1(value =>value + 1); 
                      setValue2(value =>value + 1); }}>+</button> 
<div>value1: {value1}</div> 
<div>value2: {value2}</div> 
</div> 

);

} 
export default App;

React Effect Hook: useEffect

useEffeect is a very useful hook in a lot of situations such as:

  • Replacing componentDidMount method.
  • Replacing ComponentWillUnmount method.
  • Fetching data from an API.
  • Saving values in a local storage.

Before you start using useEffect you have to import it and then pass a function inside it.

The basic usage of useEffect is: Every time the component render or re-render the function inside useEffect will get called.

import { useEffect } from 'react';

useEffect(() => {
// add your function here.
console.log('render');
})

Let’s integrate useEffect in the last example of the useState section. so our App Component will look like:

// Add useEffect

import React, { useState, useEffect } from 'react';

function App() {
const [value1, setValue1] = useState(11);
const [value2, setValue2] = useState(5);

useEffect(() => {
// add your function here.
console.log('render');
})

return (
<div>
<button onClick ={()=>{setValue1(value =>value + 1); 
                      setValue2(value =>value + 1); }}>+</button> 
<div>value1: {value1}</div> 
<div>value2: {value2}</div> 
</div> 

);

} 
export default App;

If we want the function inside the useEffect to be called only for example when the value1 changes (not for every render) all we have to do is adding a second parameter to the useEffect. This parameter is called dependencies array and contains all the values your useEffect depends on.

// Add dependency array to useEffect

import React, { useState, useEffect } from 'react';

function App() {
const [value1, setValue1] = useState(11);
const [value2, setValue2] = useState(5);

useEffect(() => {
console.log('render')}, [value1]);


return (
<div>
<button onClick ={()=>{setValue1(value =>value + 1); 
                      setValue2(value =>value + 1); }}>+</button> 
<div>value1: {value1}</div> 
<div>value2: {value2}</div> 
</div> 

);

} 
export default App;

Replacing componentDidMount method

In case you want to call the effect function only once when the component mount for the first time, you pass an empty array as a second parameter. So like this useEffect can replace componentDidMount.

//  useEffect replaces componentDidMount

import React, { useState, useEffect } from 'react';

function App() {
const [value1, setValue1] = useState(11);
const [value2, setValue2] = useState(5);

useEffect(() => {
console.log('render')}, []);


return (
<div>
<button onClick ={()=>{setValue1(value =>value + 1); 
                      setValue2(value =>value + 1); }}>+</button> 
<div>value1: {value1}</div> 
<div>value2: {value2}</div> 
</div> 

);

} 
export default App;

Replacing ComponentWillUnmount method

useEffect can also replace componentWillUnmount by returning a clean up function in side the useEffect. The clean up function can be for closing a connection after the component unmount or cleaning up an interval or a timer set already in useEffect to avoid memory leaks.

//  useEffect replaces componentWillUmount 
import React, { useEffect } from 'react';

function App() {

useEffect(() => {
console.log('render');
const interval = setInterval(() => console.log('set interval'), 1500);

return () =>{
  console.log("unmount");
  //add your clean up function here
  clearInterval(interval);
};
}, []);

return (
<div>

</div> 

);

} 
export default App;

You can have more than one useEffect in your component and they will be fired off in order.

//  Multiple useEffect in same component 
import React, { useEffect } from 'react';

function App() {

useEffect(() => {
console.log('render1');
return (( ) => {
console.log('unmount');})
}, []);

useEffect(() => {
console.log('render2');
return (( ) =>{
console.log('unmount');})
}, []);


return (
<div>

</div> 

);

} 
export default App;

Saving values in a local storage

useEffect can also be used for local storage. For example, we can save the values: value1 and value2 in the precedent example in a local storage and update them every time they change.

//  Using useEffect for local storage 
import React, { useState, useEffect } from 'react';

function App() {
const [value1, setValue1] = useState(() =>
    JSON.parse(localStorage.getItem("value1"))
);
const [value2, setValue2] = useState(() =>
    JSON.parse(localStorage.getItem("value2"))
);

useEffect(() => {
    localStorage.setItem("value1", JSON.stringify(value1));
  }, [value1]);


useEffect(() => {
    localStorage.setItem("value2", JSON.stringify(value2));
  }, [value2]);


return (
<div>
<button onClick ={()=>{setValue1(value =>value + 1); 
                      setValue2(value =>value + 1); }}>+</button> 
<div>value1: {value1}</div> 
<div>value2: {value2}</div> 
</div> 

);

} 
export default App;

Now you run npm start to open your app in the browser and click the button once or twice, then you refresh the browser. You will notice that the initial values are the precedents values because they were stored in local storage.

Fetching data from an API

The next example will show you how to use useEffect to fetch from an API. For this we are going to use axios to write our HTTP requests to the URL: ‘https://yourAPIname.com/api/employees’ in order to get the employees list.

  • The initial state of employees is an empty array.
  • You may have noticed that we passed an empty array as a dependencies array to make sure that useEffect will render only once when Component render for the first time.
  • Note that we are not using async/await in our request with useEffect because useEffect should return only another function used usually for cleaning up the effect and async functions return promises.

To learn more about how to get data from an API Using useEffect hook, check out HERE.

// Example of using useEffect to fetch data from API

import React, { useState, useEffect } from 'react'; // import useState from react.
import axios from 'axios';

function EmployeesList () {

  const [employees, setEmployees] = useState([]);

  

useEffect(() => {
    
    axios.get('https://yourAPIname.com/api/employees')
     .then(res => {
         
         console.log(res.data.status);
         setEmployees(res.data.employees);
         })

}, []); // the empty array means this effect will only run once like componentDidMount in class component

return(
//display your list here
   <div>{employees}</div>
);
}


export default EmployeesList;

React Context Hook: useContext

useContext hook is used within React applications that are using a React app context to access global state and share data deeply throughout the application. This hook accepts the context object returned from React.createContext and returns the current context value.

You cannot use useContext without first having a Context, so the first thing to do is creating a context to share between your app components. To do that, we create a new js file and named: EmployeeContext.js and import createContext from react and give it null as an initial value.

import { createContext } from "react";

export const EmployeeContext = createContext(null);

Then import the context you have just created into your App.js and wrap all the components where you want to use the EmployeeContext value in <EmployeeContext.Provider> and pass the data needed as value object into this <EmployeeContext.Provider>. By doing this, we allow the wrapped component and its children components to share this data.

The advantage of using context hook is that we can get the data no matter where the position of the component in the components tree: the component can be a child or a deep child and if we change the value in the context provider every component will get the update.

Let’s define an employee object to store the current value using useState hook and pass them as value to the Provider.

//  App.js
// import EmployeeContext  

import React, { useState } from 'react';
import {EmployeeContext} from './EmployeeContext.js';
import Child1Component from './Child1Component.js';
import Child2Component from './Child2Component.js';

function App() {

const [employee, setEmployee] = useState({name: "EmployeeName",
                                          email: "employeemail@gmail.com"
                                        });


return (
<div>
<EmployeeContext.Provider value = {{employee, setEmployee}}>
          < Child1Component />
          < Child2Component />
        </EmployeeContext.Provider>
</div> 

);

} 
export default App;

In the following code, we get the employee and setEmployee in Child1Component by using the useContext hook and passing to it the EmployeeContext we have already created. Then we display it using the JSON.stringify().

// Child1Component.js
// Getting the employee and display it inside Child1Component using useContext hook  
// import EmployeeContext

import React, { useContext } from 'react';
import {EmployeeContext} from './EmployeeContext.js';

function Child1Component () {

const {employee, setEmployee} = useContext(EmployeeContext); // get the employee


return (
<div>
<h2> Employee Info: </h2>
<pre>{JSON.stringify(employee, null, 2)}</pre> 
</div> 

);

} 
export default Child1Component;

Now you can see in your browser the employee infos displayed.

The next step is to get employee in the Child2Component then change it to null and see how Child1Component will be updated.

Let’s do the same thing to get and display the employee in Child1Component. But this time we will add a button that will change the employee to null by using the update function setEmployee.

// Child2Component.js
// Getting the employee and display it inside Child2Component using useContext hook then change to null 
// import EmployeeContext

import React, { useContext } from 'react';
import { EmployeeContext } from './EmployeeContext.js';

function Child2Component () {

const {employee, setEmployee} = useContext(EmployeeContext); // get the employee


return (
<div>
<h2> Employee Info: </h2>
<pre>{JSON.stringify(employee, null, 2)}</pre> 
<button onClick={() => setEmployee(null)}>Change Employee</button>
</div> 

);

} 
export default Child2Component;

When you click the button in your browser both component will display null.

React Reducer Hook: useReducer

useReducer is a hook used as an alternative to useState hook to deal with complex state changes in React components and use the same reducers, action, and dispatch notions used in Redux.

To use useReducer hook we need first to import it from react then call it and pass to it two parameters :

  1. A reducer function: this function in its turn takes two parameters: the state and action. The state is the current value of the state, and the action is the action that will be taken. The reducer function is usually created outside the functional component to keep it as a pure function.
  2. The initial state: represent the initial value and it can also be a function, but for a smoother code execution we avoid using functions.

The useReducer hook will return a value and a function called dispatch used to dispatch the actions in the reducer function and change the state.

//  App.js

import React, { useReducer } from 'react';

function reducer(state, action) {
//dispatch your actions here to change the state.
}

function App() {

const [value, dispatch] = useReducer (reducer, 12); // 12 here is an example of initial value


return (
<div>
</div> 

);

} 
export default App;

The basic use of useReducer Hook is to create a function that changes the state depending on the actions taken. The following example will show a basic way of using useReducer hook.

//  App.js

import React, { useReducer } from 'react';


function reducer(state, action) {
switch (action.type) {
case "double":
  return state * 2 ;
case "half":
  return state / 2;
default:
return state;
}
}

function App() {

const [value, dispatch] = useReducer (reducer, 12); // 12 here is an example of initial value


return (
<div>
  <div>The value is: {value}</div>
  <button onClick={() => dispatch({type: "double"})}> Double </button>
  <button onClick={() => dispatch({type: "half"})}> Half </button>
</div> 

);

} 
export default App;

In the code above, we displayed the current value, then we called the dispatch function with the double and half parameters. These two parameters will be passed as actions to reducer function. For a good practice, we passed to the dispatch function an object with a type key and assign to it a string parameter. Then in the reducer function we read the action type in the dispatch function and we changed the state according to it using the switch statement.

How to use React Custom Hook

Custom Hooks are normal JavaScript functions. Their names always start with use to let us know that these functions are used as Hooks. Custom hooks are used to simplify even more our code and make it more compact, cleaner and reusable by allowing us to share logic and avoid repeat the same logic inside every functional component. They are created separately and then imported into other components.

One of the most common use of custom hooks is a hook for getting data from an API. in the following we will create a custom hook called useFetch to get the employees list from an API using axios request and import it into our App.js then show you how to use it.

// Example of using useEffect to fetch data from API

import React, { useState, useEffect } from 'react'; // import useState from react.
import axios from 'axios';

function App () {

  const [employees, setEmployees] = useState([]);

  

useEffect(() => {
    
    axios.get('https://yourAPIname.com/api/employees')
     .then(res => {
         
         console.log(res.data.status);
         setEmployees(res.data.employees);
         })

}, []); // the empty array means this effect will only run once like componentDidMount in class component

return(
//display your list here
   <div>{employees}</div>
);
}


export default App;

So let’s get started, inside your project create a new file and name it useFetch.js. Now let’s define the useFetch function.

This function will take one argument, which is the url. We know that a hook can call a hook that means we can use the useEffect and useState in our custom hook after importing them in useFetch.js. We add the url as dependency to make sure that function will be called whenever the url changes.

Now the useFetch hook can be used and reused wherever you want in your app to get data from a specific url.

// useFetch.js
import React, { useState, useEffect } from 'react'; 
import axios from 'axios';

function useFetch (url)  {
  const [data, setdata] = useState([]);

useEffect(() => {
    
    axios.get(url)
     .then(res => {
         console.log(res.data.status);
         setdata(res.data);
         });

}, [url]); 

return data

}

export default useFetch;

 

To use useFetch hook we have just created inside App.js we need to import it and call it with the url ‘https://yourAPIname.com/api/employees’ to get the data. Then update the employees list using setEmployees.

Note that we don’t have to import useEffect and axios in App.js component to use useFetch hook.

// fetching data from API using Custom hook

import React, {useState} from 'react'; 
import  useFetch  from './useFetch.js';


function App () {
  const [employees, setEmployees] = useState([]);

  const data = useFetch('https://yourAPIname.com/api/employees');
  setEmployees(data.employees);



return(
    //display your list here
       <div>{employees}</div>
    );
}


export default App;

Conclusion

Hooks are an important React feature which provides an alternative way to share logic and update functional components easily. In this article we have discussed and explained the four basic React Hooks that you will probably use in each one of your React projects and we also learnt about what custom Hooks are and how to use them.

React provides a lot of other hooks such as:

  • useCallback
  • useMemo
  • useRef
  • useImperativeHandle
  • useLayoutEffect
  • useDebugValue

If you want to know more about those hooks, we suggest to checkout the official documentation.