React Hooks Fundamentals for Beginners: How to Use useState and useEffect hooks 2023
Tags: react, frontend, reactjsdevelopment, javascript
Published: 2023-06-24 12:36:10

### Table of contents 

* [What are React hooks](#what-is-react)
  * [Concept of hooks](#what-is-react)
  * [Brief overview of different hooks](#overview)
* [Managing state with the useState hook](#usestate)
  * [Explain the concept of the useState hook and how to use it to manage state. ](#usestate)
  * [Practical examples of managing state with the useState hook. ](#example-use-state)
* [Using the useEffect hook to handle side-effects in React apps](#use-effect)
  * [Explain the useEffect hook and how to use it to handle side-effects like making API calls. ](#use-effect)
  * [Practical example of making an API call to an external source. ](#api-call)
- [Refactoring Class Components to Functional Components with Hooks](#refactor)
  * [Explain how Hooks can be used to refactor class components to functional components](#refactor)
  * [Practical examples of refactoring class components to functional components with Hooks. ](#refactor-state)
- [Benefits of using React hooks](#benefits)
- [Concluding thoughts](#conclusion)

---

 
## What are react hooks <a name="what-is-react"></a>

React hooks are functions that let you use different React features from your functional components. This means that you can manage state, make API calls, and other things a class-based component can do from functional components.
 
There are different hooks in React which let you do different things. <a name="overview"></a>

- **`useState` hook** lets you create and manage state. 
- **`useEffect` hook** lets you handle side-effects in your React application 
- **`useContext` hook** lets you share state without prop drilling, awesome right? 
- **`useMemo` hook**: lets you memoize your react components
- **`useCallback` hook** is used to memoize functions, optimizing performance by preventing unnecessary re-rendering of components that depend on those functions.
- **`useReducer` hook**: this hook is similar to the useState hook but handles complex state more efficiently. 
- **`useRef` hook** provides a way to create mutable references to elements or values that persist across renders.

This is not an exhaustive list but these are the hooks you will use 99% of the time.
 
### Managing state with useState hook <a name="usestate"></a>

The useState hook lets you add and manage state in a React component. It’s equivalent to `this.state = {}` in class components. 

To use the useState hook, call it at the top-level of your component:

```jsx
const [state, setState] = useState(initialState);

```
It returns an array of exactly two values: current state and a function for updating that state.
 
Let’s look at them closely.
 
`state`: this is the current state. When your component first renders, the state equals the `initialState`.
 
`setState`: this is a function for updating the state and trigger a re-render.
 
`initialState`: the value you want the state to be initially. It can be of any valid JavaScript value. 

**Note**: The `setState` function only updates the state variable for the next render. If you read the state variable after calling the `setState` function, you will still get the old value that was on the screen before your call. 

Also, React will skip re-rendering of your component if the value you provide is identical to the current state.
 
**Usage**

To use the useState hook, you call it at the top level of your component.
 

```jsx
import {useState} from “react”
   function (){
const [state, setState] = useState(‘’);
}
```

### Basic applications of useState <a name="example-use-state"></a>
 
#### Updating a text field
 
In this simple example, the `firstName` and `lastName` state variables hold a string. When you type into the input fields, the `handleFirstName` and `handleLastName` updater functions read your inputs in the DOM and updates the UI accordingly. 

```jsx
import { useState } from 'react';

export default function MyInput() {
  const [firstName, setFirstName] = useState(‘’);
  const [lastName, setLastName] = useState(‘’);

  function handleFirstName(e) {
    setFirstName(e.target.value);
  }
function handleLastName(e) {
    setLastName(e.target.value);
  }

  return (
    <>
      <input value={firstName} onChange={handleFirstName} />
     <input value={lastName} onChange={handleLastName} />
      <p>{firstName} {lastName} is typing.</p>
    </>
  );
}
```


#### Updating objects and arrays in state

When updating arrays or objects in state, you should not directly mutate the state. 

Here’s what I mean 👇

```jsx
state.age = 42; // you should not directly mutate state.
```

Instead, you should replace the old state with a new one.
 
```jsx
SetState({
...old state, 
age: 42
})
```

This is also applicable to arrays. 

```jsx
state.push(“Banana”) // not recommended 
     setState([...prevState, “Banana”])
```

#### Example of object in state

In this example, the employee state variable holds an object. Each input field has a change handler that calls the `setEmployee` function to update the state. The spread syntax `(...)` to make sure state is replaced and not mutated. 

```jsx
import { useState } from 'react';

export default function Employee() {
  const [employee, setEmployee] = useState({
    firstName: “”,
    lastName: “”,
    job: '',
  });

  return (
    <>
      <label>
        First name:
        <input
          value={employee.firstName}
          onChange={e => {
            setEmployee({
              ...employee,
              firstName: e.target.value
            });
          }}
        />
      </label>
      <label>
        Last name:
        <input
          value={employee.lastName}
          onChange={e => {
            setEmployee({
              ...employee,
              lastName: e.target.value
            });
          }}
        />
      </label>
      <label>
        Job:
        <input
          value={employee.job}
          onChange={e => {
            setEmployee({
              ...employee,
              job: e.target.value
            });
          }}
        />
      </label>
      <p>
        {employee.firstName}{' '}
        {employee.lastName}{' '}
        ({employee.job})
      </p>
    </>
  );
}
```


#### Example of array in state

In this example, the fruits state variable holds an array. Each button handler that calls the `setFruits` function to update the state. The spread syntax (...) to make sure state is replaced and not mutated. 

```jsx
import { useState } from 'react';
import AddTodo from './AddTodo.js';
import TaskList from './TaskList.js';

let nextId = 3;
const initialTodos = [
  { id: 0, title: 'Buy milk', done: true },
  { id: 1, title: 'Eat tacos', done: false },
  { id: 2, title: 'Brew tea', done: false },
];

export default function TaskApp() {
  const [todos, setTodos] = useState(initialTodos);

  function handleAddTodo(title) {
    setTodos([
      ...todos,
      {
        id: nextId++,
        title: title,
        done: false
      }
    ]);
  }

  function handleChangeTodo(nextTodo) {
    setTodos(todos.map(t => {
      if (t.id === nextTodo.id) {
        return nextTodo;
      } else {
        return t;
      }
    }));
  }

  function handleDeleteTodo(todoId) {
    setTodos(
      todos.filter(t => t.id !== todoId)
    );
  }

  return (
    <>
      <AddTodo
        onAddTodo={handleAddTodo}
      />
      <TaskList
        todos={todos}
        onChangeTodo={handleChangeTodo}
        onDeleteTodo={handleDeleteTodo}
      />
    </>
  );
}

```

### Handling side effects with `useEffect` hook <a name="use-effect"></a>

The `useEffect` hook is used to handle side effects in React. Side effects mean operations not controlled by React. Common side effects include:

- Making calls to a third-party API
- Interacting with the browser’s local storage 
- Communicating with the DOM
- Implementing certain libraries such as: animation libraries. 

To use the `useEffect` hook, declare it at the top-level of your component 

```jsx
import React, { useEffect } from 'react';

function ExampleComponent() {
  useEffect(() => {
    // Side effect code goes here

    // Clean up the effect (optional)
    return () => {
      // Cleanup code goes here
    };
  }, []);

  return (
    // Component JSX goes here
  );
}


export default ExampleComponent;
```

The `useEffect` hook takes two arguments:

The first argument is a callback function that contains the code for the side effect. This is where you can perform API calls, manipulate the DOM, interact with browser storage, or integrate with external libraries.

The second argument is the dependency array, which determines when the effect should run. By passing an empty array `[]`, the effect will only run once, after the initial render. 
If you want the effect to run again when certain dependencies change, you can include them in the dependency array.

Now that you have a good understanding of the `useEffect` hook and its purpose in handling side effects in React, let's look at some common use cases and see how it can be used in practice.
 
#### Making calls to third-party APIs <a name="api-call"></a>

In this example, we'll look at how to use the `useEffect` hook in React to make calls to third-party APIs. Making API calls is something you will do more often as a front-end developer, and the `useEffect` hook provides a convenient way to handle such asynchronous operations.

We are going to use the IMDB API to retrieve details of a film. 
Let's dive into the code to see how it's done.

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

function FilmDetails() {
  const [film, setFilm] = useState(null);

  useEffect(() => {
    // Function to fetch film details from the IMDB API
    const fetchFilmDetails = async () => {
      try {
        const response = await fetch('https://api.example.com/films/123');
        const data = await response.json();
        setFilm(data);
      } catch (error) {
        console.error('Error fetching film details:', error);
      }
    };

    fetchFilmDetails();

    // Cleanup function
    return () => {
      // Cleanup code goes here
      // This will be executed when the component is unmounted or when the dependency changes
    };
  }, []);

  if (!film) {
    return <p>Loading film details...</p>;
  }

  return (
    <div>
      <h2>{film.title}</h2>
      <p>{film.description}</p>
      {/* Render other film details */}
    </div>
  );
}

export default FilmDetails;

```
In this example, we have a functional component, `FilmDetails`. We also use the `useState` hook to manage the `film` state which is initially set to `null`.
Inside the `useEffect` hook, we define an asynchronous function to fetch data from the IMDB API. 

If the API call succeeds, we update the `film` state with the retrieved data using `setFilm`. 

If an error occurs during the API call, we log the error message to the console within the `catch` block.

We passed an empty dependency array as the second argument. This means that the effect will run only once—on initial render. 

For better user experience, we have added a loading state in case the film data is yet available. Once it is available, we render the component JSX. 

Finally, the `return` statement inside the `useEffect` returns a clean-up function, which will be executed when the component is unmounted or when the dependency changes. You can add any necessary clean-up code, such as canceling ongoing requests or removing event listeners, inside the clean-up function.

If the API call was successful, you should see the fetched film details displayed in your browser.

#### Interacting with the browser's local storage <a name="local-storage"></a>
 
In this example, we are going to reading and writing to the browser's local storage. 
The browser's local storage is used for persisting data.
 
Let's dive into the code. 

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

function LocalStorageExample() {
  const [name, setName] = useState('');

  useEffect(() => {
    // Read from local storage
    const storedName = localStorage.getItem('name');
    if (storedName) {
      setName(storedName);
    }
  }, []);

  const handleNameChange = (e) => {
    const newName = e.target.value;
    setName(newName);

    // Write to local storage
    localStorage.setItem('name', newName);
  };

  return (
    <div>
      <h2>Hello, {name || 'stranger'}!</h2>
      <input type="text" value={name} onChange={handleNameChange} />
    </div>
  );
}

export default LocalStorageExample;
```

In this example, we define a functional component, `LocalStorageExample`. We use the `useState` hook to manage the `name` state.

We use the `useEffect` hook to interact with the local storage. First, we read from the local storage using the `getItem()` method. If `name` key exists, we set the `name` state to the value using the `setName` function. 
Next, we define a `handleNameChange` function that is triggered when the `input`'s value changes. It updates the name state with the new value and uses `localStorage.setItem()` to write the new value to the local storage, associating it with the key 'name'.
 
The component renders a heading that displays the greeting with the current name (or `stranger` if no name is set), and an input field where the user can enter a new name.

**Note**: This code will not work in server-side as the localStorage only exists in the browser. 

## Refactoring Class Components to Functional Components with Hooks <a name="refactor"></a>

Hooks were only introduced in React 16. This means that if you have been using React before then, you will most likely be using class components for tracking and managing data, as well as fetching and sending data.
 
In this section, I am going to show you how to refactor your class components to functional components using hooks. 

### Refactoring State Management: From Class Components to Functional Components <a name="refactor-state"></a>

To refactor class components with state to functional components with state, we convert the class component's state management using `this.state` and `this.setState` to functional components utilizing the useState hook.

Class component

```jsx
import React, { Component } from 'react';

class ClassComponentWithState extends Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0,
    };
  }

  incrementCount = () => {
    this.setState({ count: this.state.count + 1 });
  };

  render() {
    return (
      <div>
        <p>Count: {this.state.count}</p>
        <button onClick={this.incrementCount}>Increment</button>
      </div>
    );
  }
}

export default ClassComponentWithState;
```

Corresponding functional component 

```jsx
import React, { useState } from 'react';

function FunctionalComponentWithState() {
  const [count, setCount] = useState(0);

  const incrementCount = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={incrementCount}>Increment</button>
    </div>
  );
}

export default FunctionalComponentWithState;
```

We converted the class component  to functional component by replacing `this.state` and `this.setState` with the `useState` hook. As explained above, the `useState` hook returns an array with exactly two values: current state and an updated function. 

### Refactoring Data Fetching: From Class Components to Functional Components <a name="refactor-data-fetch"></a>

To refactor data fetching from class components to functional components, we convert the class component's approach, which typically uses lifecycle methods like `componentDidMount` and `componentDidUpdate`, to functional components utilizing the `useEffect` hook for handling data fetching and updating component state.

Class component with data fetching 

```jsx
import React, { Component } from 'react';

class ClassComponentWithDataFetching extends Component {
  constructor(props) {
    super(props);
    this.state = {
      data: [],
      updateCount: 0,
    };
  }

  componentDidMount() {
    // Data fetching
    this.fetchData();
  }

  componentDidUpdate(prevProps, prevState) {
    // Check if updateCount has changed
    if (prevState.updateCount !== this.state.updateCount) {
      this.fetchData();
    }
  }

  fetchData = () => {
    // Data fetching
    fetch('https://api.example.com/data')
      .then((response) => response.json())
      .then((jsonData) => this.setState({ data: jsonData }))
      .catch((error) => console.error('Error fetching data:', error));
  };

  handleUpdate = () => {
    this.setState((prevState) => ({
      updateCount: prevState.updateCount + 1,
    }));
  };

  render() {
    return (
      <div>
        {this.state.data.map((item) => (
          <p key={item.id}>{item.name}</p>
        ))}
        <button onClick={this.handleUpdate}>Update</button>
      </div>
    );
  }
}

export default ClassComponentWithDataFetching
```

Functional component with data fetching


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

function FunctionalComponentWithDataFetching() {
  const [data, setData] = useState([]);
  const [updateCount, setUpdateCount] = useState(0);

  useEffect(() => {
    // Data fetching
    const fetchData = async () => {
      try {
        const response = await fetch('https://api.example.com/data');
        const jsonData = await response.json();
        setData(jsonData);
      } catch (error) {
        console.error('Error fetching data:', error);
      }
    };

    fetchData();
  }, [updateCount]); // Update when updateCount changes

  // Simulating component update triggering the useEffect
  const handleUpdate = () => {
    setUpdateCount((prevCount) => prevCount + 1);
  };

  return (
    <div>
      {data.map((item) => (
        <p key={item.id}>{item.name}</p>
      ))}
      <button onClick={handleUpdate}>Update</button>
    </div>
  );
}

export default FunctionalComponentWithDataFetching;

```

We converted the class component by replacing the `componentDidMount` and`componentDidUpdate`with the `useEffect` hook. The dependency array helps us mimick the two lifecycle methods. 

## Benefits of Using the React hooks <a name="benefits"></a>

Using React hooks provides several benefits in developing React applications:

1. **Simplicity**
Hooks enable functional components to have their own state and lifecycle methods. This makes it easier to read, write, and understand code without dealing with the complexity of class components.

2. **Reusability** 
Hooks promote code reuse. Custom hooks allow you to encapsulate logic and stateful behavior into reusable functions that you used across multiple components in your application. This promotes modular and maintainable code.

3. **Enhanced Functional Components**
Hooks enable functional components to have state and side effects, previously only available in class components. You can manage component state using the `useState` hook and handle side effects with the `useEffect` hook, making functional components more powerful and versatile.

4. **Improved Performance**
Hooks optimize performance by reducing unnecessary re-renders. With the `useMemo` and `useCallback` hooks, you can memoize values and functions respectively, preventing unnecessary computations or re-rendering of components.

5. **Easier Testing**
Hooks promote functional programming concepts which makes testing easier, as the logic is separated into small, testable functions. This allows you to test components in isolation. 

6. **Migration from Class Components**
Hooks simplify the process of migrating from class components to functional components. Existing class components can be refactored to functional components using hooks, retaining the functionality while improving code readability and maintainability.

7. **Community Support**
Hooks have gained widespread adoption and support from the React community. This means that you can find ample resources, documentation, and community-driven libraries to assist in learning and using hooks effectively.

---

## Concluding Thoughts <a name="conclusion"></a>

React hooks are a powerful feature that allows developers to utilize different React functionalities within functional components. They enable state management, handling side effects, and refactoring class components to functional components. 

The useState hook is used for managing state, while the useEffect hook is used for handling side effects such as making API calls. Hooks provide benefits such as code simplicity, reusability, and eliminating the need for class components. 

Overall, React hooks offer an efficient and effective way to enhance the functionality and development experience in React applications.

Follow me on [Twitter](https://twitter.com/chideracode) and [LinkedIn](https://linkedin.com/in/derajs) for more content and updates.