React Hooks: useState and useEffect in React.js Web Application

React Hooks: useState and useEffect in React.js Web Application

In this React Hooks tutorial, you'll see the real-world examples of React Hooks useState and useEffect in a React.js Web application. This React.js tutorial: React Hooks useState and useEffect Examples

The real-world examples of React Hooks useState and useEffect in a React.js Web application

In this React.js tutorial, we will show you the real-world examples of React Hooks useState and useEffect in a React.js Web application. Now, with these examples you will learn more deeply about a few essential React Hooks useState and useEffect.

Table of Contents:

  • Preparation
  • React Hooks useState Examples
  • React Hooks useEffect Examples
  • Run and Test the React Hooks useState and useEffect Examples

The following tools, frameworks, libraries, and modules are required for this tutorial:

  1. Node.js (with NPM or Yarn)
  2. React.js (min. version 16.8)
  3. Node-Express REST API
  4. Terminal or Node Command Line
  5. IDE or Text Editor (We are using Visual Studio Code)
Preparation

We need to prepare the environment for creating React.js application from the scratch, we assume you don't have any tools or frameworks installed on your computer. The first thing to do is install Node.js with NPM or Yarn. Just download the installer from official download page https://nodejs.org, choose recommended version for your OS. Install to your computer by following the install wizard. After Node.js installation finished, open your terminal (Mac OS/Linux) or Node command line (Windows) then check the installed Node.js and NPM.

node -v
v10.15.1
npm -v
6.13.1

If you plan to use Yarn, install it from the Terminal. On our Mac OS, we type this command.

brew install yarn

You can choose your desired installation method for your OS, you find the guide on the Yarn site. To check the installed yarn version, type this command.

yarn -v
1.19.2

Next, we will use the create-react-app tool. The create-react-app is a tool to create a React.js app from the command line or CLI. So you don’t need to install or configure tools like Webpack or Babel because they are preconfigured and hidden so that you can focus on the code. Type this command to install it.

sudo npm install -g create-react-app

Now, we can create a new React.js app using that tool.

create-react-app react-hooks-use

This command will create a new React app with the name react-hooks-use and this process can take minutes because all dependencies and modules also installing automatically. Next, go to the newly created app folder.

cd ./react-hooks-use

Open the project in your IDE or text editor and see the content of package.json.

"dependencies": {
  "react": "^16.12.0",
  "react-dom": "^16.12.0",
  "react-scripts": "3.2.0"
},

That React version is the version that already uses React Hooks as default. Now, src/App.js doesn't use class anymore. For sanitation, run this React app for the first time by type this command.

yarn start

And here' we go the latest React.js application that uses React Hooks with the same initial home page.

Next, we need to add the required component or page for each example. For that, add these directories and files inside the src directory.

mkdir src/hooks-state
mkdir src/hooks-effect
touch src/hooks-state/BasicState.js
touch src/hooks-state/MultipleState.js
touch src/hooks-effect/BasicEffect.js
touch src/hooks-effect/FetchEffect.js
touch src/hooks-effect/AxiosEffect.js
touch src/hooks-effect/ListenerEffect.js

Next, add the react-router-dom to make routing and navigation for these examples.

yarn add react-router-dom

Next, open and edit src/index.js then add these imports of Router and Route (react-router-dom) and all created components before the ./index.css import.

import { BrowserRouter as Router, Route } from 'react-router-dom';
import BasicState from './hooks-state/BasicState';
import MultipleState from './hooks-state/MultipleState';
import BasicEffect from './hooks-effect/BasicEffect';
import FetchEffect from './hooks-effect/FetchEffect';
import AxiosEffect from './hooks-effect/AxiosEffect';
import ListenerEffect from './hooks-effect/ListenerEffect';

Next, add to the React.DOM render.

ReactDOM.render(
    <Router>
        <div>
            <Route render ={()=> < App />} path="/" />
            <Route render ={()=> < BasicState />} path="/hooks-state/basic-state" />
            <Route render ={()=> < MultipleState />} path="/hooks-state/multiple-state" />
            <Route render ={()=> < BasicEffect />} path="/hooks-effect/basic-effect" />
            <Route render ={()=> < FetchEffect />} path="/hooks-effect/fetch-effect" />
            <Route render ={()=> < AxiosEffect />} path="/hooks-effect/axios-effect" />
            <Route render ={()=> < ListenerEffect />} path="/hooks-effect/listener-effect" />
        </div>
    </Router>, document.getElementById('root'));

Next, we will create navigation for all React Hooks useState and useState examples. Open and edit src/App.js then replace all "return" content with these.

return (
  <div className="App">
    <h2>React Hooks Examples</h2>
    <h3>useState Examples:</h3>
    <p><a href="/hooks-state/basic-state">Basic useState Example</a></p>
    <p><a href="/hooks-state/multiple-state">Multiple useState Example</a></p>
    <h3>useEffect Examples:</h3>
    <p><a href="/hooks-effect/basic-effect">Basic useEffect Example</a></p>
    <p><a href="/hooks-effect/fetch-effect">Fetch useEffect Example</a></p>
    <p><a href="/hooks-effect/axios-effect">Axios useEffect Example</a></p>
    <p><a href="/hooks-effect/listener-effect">Listener useEffect Example</a></p>
    <p><a href="/hooks-effect/resize-effect">Resize useEffect Example</a></p>
  </div>
);

Next, make all components file have an exported function name same as the file name as an example below.

import '../App.css';

function BasicState() {

  return (
    <div className="App">
    </div>
  );
}

export default BasicState;
React Hooks useState Examples

Before seeing the example, we try to make a little understanding of the React Hooks useState formula. It just simply like this line.

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

Which "state" is a variable, "setState" if a function that can change the variable, and "initialState" is an initial value of the variable.

Basic useState Example

Next, we need to implement the React Hooks basic useState. Open and edit src/hooks-state/BasicState.js then add useState to the react import.

import React, { useState } from 'react';

We will declare a variable of the string that has an initial value.

const [title, setTitle] = useState('Getting Started learning a React Hooks');

In this basic useState usage, we will put the "setTitle" that change the "title" value as a button action. Add/replace these lines to the React template to add a text and a button.

return (
  <div className="App">
    <h2>{title}</h2>
    <button onClick={() => setTitle('React Hooks in My Learning Path')}>
      Change Title
    </button>
  </div>
);

Multiple useState Example

This example is the multiple "useState" with different data types. Open and edit src/hooks-state/MultipleState.js then add useState to the react import.

import React, { useState } from 'react';

Add these multiple constant variables.

const [title, setTitle] = useState('Getting Started learning a React Hooks');
const [year, setYear] = useState(2019);
const [emails, setEmail] = useState([
  {name: 'my name', email: '[email protected]'},
  {name: 'my second name',email: '[email protected]'},
  {name: 'my third name',email: '[email protected]'},
  {name: 'my alias name',email: '[email protected]'}
]);

Replace the "return" content with these.

return (
  <div className="App">
    <h2>{title}</h2>
    <h2>{year}</h2>
    <dl>
      {emails.map((item, idx) => (
        <dd>{item.name}, {item.email}</dd>
      ))}
    </dl>
    <button onClick={() => {
      setTitle('Another React Hooks Book');
      setYear(2020);
      setEmail([
        {name: 'Ian Rush', email: '[email protected]'},
        {name: 'Roberto Baggio',email: '[email protected]'},
        {name: 'Thierry Hendry',email: '[email protected]'},
        {name: 'David Seaman',email: '[email protected]'}
      ]);
    }}>
      Change Title
    </button>
  </div>
);
React Hooks useEffect Examples

React Hooks useEffect is a single function that similar to componentDidMount, componentDidUpdate, and componentWillUnmount in React classes. We will show you an example of the useEffect in a real React application.

Basic useEffect Example

This basic useEffect example just changes the title when the component is loaded. Open and edit src/hooks-effect/BasicEffect.js then add this import of useState and useEffect.

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

Add a required constant variable that uses useState.

const [title, setTitle] = useState('React Hooks at a Glance');
const [year, setYear] = useState(2019);

Add this useEffect function before the return template.

useEffect(() => {
  setTitle(`The new articles of ${year}`);
});

Change the return content with these.

return (
  <div className="App">
    <h2>{title}</h2>
  </div>
);

Fetch useEffect Example

This example fetches data from the REST API you can run our Node-Express REST API and MongoDB server in your machine to implementing this example. Open and edit src/hooks-effect/FetchEffect.js then add this import of useState and useEffect.

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

Add a required constant variable that use useState.

const [product, setProduct] = useState(null);
const [url, setState] = useState(`http://localhost:3000/api/v1/products`);

Next, we will implement useEffect with a cleaning-up effect feature by adding an AbortController method at the useEffect return. Add this useEffect function after the constant variable.

useEffect(() => {

  let abortController = new AbortController();

  const loadProduct = async () => {
    try {
      const response = await fetch(url, { signal: abortController.signal });
      const data = await response.json();
      console.log(`Data fetched: ${data}`);
      setProduct(data);
    } catch (error) {
      console.log(`Error fetch data: ${error}`);
      throw error;
    }
  };

  loadProduct();

  return () => {
    console.log("Cancel fetch: abort");
    abortController.abort();
  };
}, [url]);

If you want to implement conditionally firing an effect, just change the end of the useEffect function to this.

useEffect(() => {
  ...
}, [product]);

Next, add a loading indicator before implementing the view template.

if (!product) {
  return <div className="App">Loading...</div>;
}

Modify the return to display the loaded data.

return (
  <div className="App">
    {product.map((item, idx) => (
      <p>{item.prod_name}</p>
    ))}
  </div>
);

Axios useEffect Example

This example same as the previous fetch example except we are using the Axios library to consume the REST API. First, we need to install that library by type this command in the terminal.

yarn add axios

Next, open and edit src/hooks-effect/AxiosEffect.js then add these imports of useState, useEffect, and Axios.

import React, { useState, useEffect } from 'react';
import Axios from "axios";

Add a required constant variable that uses useState.

const [product, setProduct] = useState(null);
const [url, setState] = useState(`http://localhost:3000/api/v1/products`);

Next, we will implement useEffect with a cleaning-up effect feature by adding an Axios.CancelToken.source() cancel method at the useEffect return. Add this useEffect function after the constant variable.

useEffect(() => {
  let source = Axios.CancelToken.source();

  const loadProduct = async () => {
    try {
      const response = await Axios.get(url, {
        cancelToken: source.token
      });
      console.log(response);
      setProduct(response.data);
    } catch (error) {
      if (Axios.isCancel(error)) {
        console.log("Cancel axios data source on error");
      } else {
        throw error;
      }
    }
  };

  loadProduct();

  return () => {
    console.log("Cancel axios data source");
    source.cancel();
  };
}, [url]);

Next, add a loading indicator before implementing the view template.

if (!product) {
  return <div className="App">Loading...</div>;
}

Modify the return to display the loaded data.

return (
  <div className="App">
    {product.map((item, idx) => (
      <p>{item.prod_name}</p>
    ))}
  </div>
);

Listener useEffect Example

This example of React Hook's useEffect is firing an effect based on Javascript DOM event listener. Right now, we will use the click event lister for the windows or screen element. Open and edit src/app/hooks-effect/ListenerEffect.js then add this import of useEffect.

import React, { useEffect } from 'react';

Next, we will implement the useEffect that firing by click event listener.

useEffect(() => {
  const listener = () => {
    console.log(`The screen is clicked`);
  };
  window.addEventListener("click", listener);

  return () => {
    window.removeEventListener("click", listener);
  };
}, []);

Next, modify the return to this HTML template.

return (
  <div className="App">
    <h4>Click the screen to listen the click event</h4>
  </div>
);
Run and Test the React Hooks useState and useEffect Examples

To run this React Hooks useState and useEffect examples, we need to run MongoDB server and Node-Express REST API first in another terminal tabs. Next, run this React Hooks example using custom port by type this command.

PORT=8080 yarn start

Now, the React Hooks useState and useEffect Examples run using Port 8080 that different from the Node-Express server. You can check the implementation of these examples one by one from this React home page.

That it's, the React Hooks useState and useEffect Examples. You can find the full source codes from our GitHub.

Thanks!

React Hooks Tutorial for Beginners: Getting Started With React Hooks

React Hooks Tutorial for Beginners: Getting Started With React Hooks

React hooks tutorial for beginners, learn React hooks step by step: Introduction, useState Hook, useState with previous state, useState with object, useState with array, useEffect Hook, useEffect after render, Conditionally run effects, Run effects only once, useEffect with cleanup, useEffect with incorrect dependency, Fetching data with useEffect, useContext Hook, useReducer Hook, useReducer, Multiple useReducers, useReducer with useContext, Fetching data with useReducer, useState vs useReducer, useCallback Hook, useMemo Hook, useRef Hook

React Hooks Tutorial - 1 - Introduction

Hooks are a new addition in React 16.8. They let you use state and other React features without writing a class.

React Hooks Tutorial - 2 - useState Hook React Hooks Tutorial - 3 - useState with previous state React Hooks Tutorial - 4 - useState with object React Hooks Tutorial - 5 - useState with array React Hooks Tutorial - 6 - useEffect Hook React Hooks Tutorial - 7 - useEffect after render React Hooks Tutorial - 8 - Conditionally run effects React Hooks Tutorial - 9 - Run effects only once React Hooks Tutorial - 10 - useEffect with cleanup React Hooks Tutorial - 11 - useEffect with incorrect dependency React Hooks Tutorial - 12 - Fetching data with useEffect Part 1 React Hooks Tutorial - 13 - Fetching data with useEffect Part 2 React Hooks Tutorial - 14 - Fetching data with useEffect Part 3 React Hooks Tutorial - 15 - useContext Hook Part 1 React Hooks Tutorial - 16 - useContext Hook Part 2 React Hooks Tutorial - 17 - useContext Hook Part 3 React Hooks Tutorial - 18 - useReducer Hook React Hooks Tutorial - 19 - useReducer (simple state & action) React Hooks Tutorial - 20 - useReducer (complex state & action) React Hooks Tutorial - 21 - Multiple useReducers React Hooks Tutorial - 22 - useReducer with useContext React Hooks Tutorial - 23 - Fetching data with useReducer Part 1 React Hooks Tutorial - 24 - Fetching data with useReducer Part 2 React Hooks Tutorial - 25 - useState vs useReducer React Hooks Tutorial - 26 - useCallback Hook React Hooks Tutorial - 27 - useMemo Hook React Hooks Tutorial - 28 - useRef Hook Part 1 React Hooks Tutorial - 29 - useRef Hook Part 2 React Hooks Tutorial - 30 - Custom Hooks React Hooks Tutorial - 31 - useDocumentTitle Custom Hook React Hooks Tutorial - 32 - useCounter Custom Hook React Hooks Tutorial - 33 - useInput Custom Hook

How to create reusable React components with React Hooks?

How to create reusable React components with React Hooks?

In this React components tutorial, we learn how to create reusable React components with React Hooks. Building reusable UI components with React Hooks. We’ll use Hooks for managing and manipulating state data.

In React, a reusable component is a piece of UI that can be used in various parts of an application to build more than one UI instance. For instance, we can have a button component display with different colors in several parts of our application. Although it is the same button component when we provide it with a dataset (e.g color, or a function), it modifies itself and outputs a UI instance of the element.

This pattern of creating React components is necessary for scaling. It helps save time by ensuring less code is written, development is faster, the codebase is simpler, and maintenance is stress-free.

In this tutorial, we will build reusable React components that can be used throughout your projects to maintain consistency in logic and presentation. We’ll use Hooks for managing and manipulating state data.

This post assumes you that you have a basic understanding of React. If you don’t have any experience using React, you can refer to the documentation here to get started.

Getting started

In this part of the tutorial, we will create various HTML elements as reusable components. We’ll be using CodeSandbox. You can create a new sandbox to get started:

Input component

One advantage of creating a reusable input component is that you maintain the appearance of the input in various parts of your application. You can also determine what type of input component should be rendered (text, email, etc) by passing it a prop. Although we won’t go deep into styling in this tutorial, you can customize your components to suit your visual needs.

In your newly created sandbox project, create a components folder with a FormInput.js file and add the following code to it:

// ./components/FormInput.js
import React, {useState} from "react";

function FormInput(props) {
  const [inputType] = useState(props.type)
  const [inputValue, setInputValue] = useState('')

  function handleChange(event){
    setInputValue(event.target.value);
    if(props.onChange) props.onChange(inputValue)
  }
  return (
    <>
      <input type={inputType} value={inputValue} name="input-form" onChange={handleChange} class="inputclass"/>
    </>
  );
}
export default TextInput;

For a component to be reusable, it has to take in data or data sets (via props) and return an output (usually through a function passed via props). It is recommended that mutable state should be kept in the state property of a component to ensure they work correctly.

The FormInput() component above receives an input type to determine what type of input element to render (email, text, etc). It also takes in a method onChange() to receive the value sent back out from the input.

The component manages its value locally, and only returns the updated state value to the component it is called from.

To achieve this, we created a local function handleChange(). The function checks if a method to receive the state data is available via props then sends the current state data to it for further processing.

Custom select component

In your components folder, create a CustomSelect.js file and add the following code to it:

// ./components/CustomSelect.js
import React, { useState } from "react";
function CustomSelect(props) {
  const [data] = useState(props.data);
  const [selectedData, updateSelectedData] = useState("");
  function handleChange(event) {
    updateSelectedData(event.target.value);
    if (props.onSelectChange) props.onSelectChange(selectedData);
  }
  let options = data.map(data => (
    <option key={data.id} value={data.id}>
      {data.name}
    </option>
  ));
  return (
    <select
      name="customSearch"
      className="custom-search-select"
      onChange={handleChange}
    >
      <option>Select Item</option>
      {options}
    </select>
  );
}
export default CustomSelect;

Above, we receive the data set needed for the options tag in the select element via props. To build the option tags, we looped through the data set via props to construct it before rendering it as part of the select tag.

The state of the tag (the currently selected option) is stored locally and updated and sent back as an output when it changes via our local function handleChange().

Button component

A reusable button can be used to display different color variants or sizes everywhere it is used in your application. In your components folder, create a Button.js file and add the following code to it:

// ./components/Button.js
import React, { useState } from "react";
function Button(props) {
  const [size] = useState(props.size);
  const [variant] = useState(props.variant);
  return (
    <button className={`btn-${variant} btn-${size}`}>{props.children}</button>
  );
}
export default Button;

Our button receives three properties via props. The variant (used to determine the button color), the size (lg, xs, sm) to determine the size of the button. We display the button content dynamically using React’s built-in children property (props.children).

Modal component

A modal component is suitable for sending alerts in your application. In your components folder, create a Modal.js file and add the following code to it:

// ./components/Modal.js
import React, { useState, useEffect } from "react";
function Modal(props) {
  const [message] = useState(props.message);
  const [show, setShow] = useState(props.show);
  useEffect(() => {
    setTimeout(() => {
      setShow(false);
    }, 3000);
  });
  return (
    <div className={show ? "modal display-block" : "modal display-none"}>
      <section className="modal-main">{message}</section>
    </div>
  );
}
export default Modal;

Our modal component does two things:

  • It receives a boolean value that determines if it pops up or not
  • It also receives the message to be displayed when it pops up

To close the modal, we’d need to set the show state to false. We can do that by calling a setTimeout() function in the [useEffect()](https://reactjs.org/docs/hooks-effect.html) hook after a few seconds.

Toggle component

A toggle component is used in situations where a true or false answer is necessary. It is an essential form component.

In your components folder, create a ToggleSwitch.js file and add the following code to it:

// ./components/ToggleSwitch.js
import React, { useState } from "react";
function ToggleSwitch(props) {
  const [checked, setChecked] = useState(props.defaultChecked);
  const [Text] = useState(props.Text);
  function onChange(e) {
    setChecked(e.target.value);
    if (props.onToggleChange) props.onToggleChange(checked);
  }
  return (
    <div className={"toggle toggle-switch"}>
      <input
        type="checkbox"
        name={props.Name}
        className="toggle-switch-checkbox"
        id={props.id}
        defaultChecked={checked}
        onChange={onChange}
      />
      {props.id ? (
        <label className="toggle-switch-label" htmlFor={props.id}>
          <span
            className={
              props.disabled
                ? "toggle-switch-inner toggle-switch-disabled"
                : "toggle-switch-inner"
            }
            data-yes={Text[0]}
            data-no={Text[1]}
          />
          <span
            className={
              props.disabled
                ? "toggle-switch-switch toggle-switch-disabled"
                : "toggle-switch-switch"
            }
          />
        </label>
      ) : null}
    </div>
  );
}
export default ToggleSwitch;

Our toggle component receives the following props:

  • ID (required): this is the ID that’s going to be passed to the checkbox input control. Without this, the component won’t render
  • Text (required): The Toggle Switch contains an array of two values, which signify the text for True and False
  • Name (optional): this will be label text of the checkbox input
  • onChange (optional): this will used to receive the returned data from the components
  • Checked (optional): this will be directly passed to the element to get its current state
  • Disabled (optional): this will be be used to set the state of the button

When it changes, we update the state and send the value to the event listener sent via props from the parent component.

Using the components

To use the components we just created, we need to render them from a parent component and pass the relevant data to them. Add the following to your index.js:

// ./index.js
import React, { useState } from "react";
import ReactDOM from "react-dom";
import FormInput from "../components/FormInput.js";
import CustomSelect from "../components/CustomSelect.js";
import ToggleSwitch from "../components/ToggleSwitch.js";
import Button from "../components/Button.js";
import Modal from "../components/Modal.js";
import "./styles.css";
function App() {
  const data = [
    {
      id: "1",
      name: "One"
    },
    {
      id: "2",
      name: "Two"
    },
    {
      id: "3",
      name: "Three"
    },
    {
      id: "4",
      name: "Four"
    }
  ];
  function handleChange(event) {
    console.log(event.target.value);
  }
  function onSelectChange(event) {
    console.log(event.target.value);
  }
  function onToggleChange(event) {
    console.log(event.target.checked);
  }
  return (
    <div className="App">
      <form>
        <Modal show={true} message={"Hello"}>
          <p>THIS IS A MODAL</p>
        </Modal>
        <FormInput type={"text"} onChange={handleChange} />
        <FormInput type={"email"} onChange={handleChange} />
        <div>
          <CustomSelect data={data} onSelectChange={onSelectChange} />
        </div>
        <div>
          <ToggleSwitch
            id="id"
            defaultChecked={false}
            disabled={false}
            Text={["Yes", "No"]}
            onToggleChange={onToggleChange}
          />
        </div>
        <Button variant="danger" size={"sm"} >Small Button</Button>
        <Button variant="primary" size={"lg"} >Smaller Button</Button>
        <Button variant="warning" size={"xs"} >Big Button</Button>
      </form>
    </div>
  );
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

And you should see the following:

Conclusion

In this tutorial, we learned how to create reusable React components. This knowledge can be applied to create highly scalable and consistent React applications. We also worked with Hooks to manage and manipulate state data. You can find all the code samples used in this post in the this CodeSandbox project:

To read more about Hooks and React components check out the docs here.

Introduction to React Hooks

Introduction to React Hooks

Introduction to React Hooks. Introducing Hooks. What are Hooks? Hooks are a new addition in React 16.8. They let you use state and other React features without writing a class. This new function useState is the first “Hook” we'll learn about, but this example is just a teaser.

What are Hooks anyway?

React Hooks let you use state, and other React features without having to define a JavaScript class. It’s like being able to take advantage of the cleanliness and simplicity of a Pure Component and state and component lifecycle methods. This is because Hooks are just regular JavaScript functions! This lends itself to cleaner and less clunky code. A side by side comparison of what the code looks like with and without Hooks for a simple counting component:

import './App.css';
import React, { useState } from 'react';

const HooksExample = () => {
    const [counter, setCount] = useState(0);

    return (
        <div className="App">
            <header className="App-header">
                The button is pressed: { counter } times.
                <button
                    onClick={() => setCount(counter + 1)}
                    style={{ padding: '1em 2em', margin: 10 }}
                >
                    Click me!
                </button>
            </header>
        </div>
    )
}

export default HooksExample;

NoHooks.js:

import './App.css';
import React, { Component } from 'react';

export class NoHooks extends Component {
    constructor(props) {
        super(props;
        this.state = {
            counter: 0
        }
    }

    render() {
        const { counter } = this.state;
        return (
            <div className="App">
                <header className="App-header">
                    The button is pressed: { counter } times.
                    <button
                        onClick={() => this.setState({ counter: counter + 1 }) }
                        style={{ padding: '1em 2em', margin: 10 }}
                    >
                        Click me!
                    </button>
                </header>
            </div>
        )	
    }
}

export default NoHooks;

Not only is the code a lot smaller — the saved space certainly adds up for larger components — it’s also a lot more readable, which is a huge advantage of Hooks. For beginners who are just getting started with React, it’s easier for them to read the first block of code and easily see exactly what’s happening. With the second block, we have some extraneous elements, and it’s enough to make you stop and wonder what it’s for.

Another great thing about hooks is that you can create your own! This means that a lot of the stateful logic we used to have to re-write from component to component, we can now abstract out to a custom hook — and reuse it.

The one example where this is particularly life-changing (for me) that comes to mind is use with forms. With all of the stateful logic of forms, it’s hard to reduce the size of the component. But now, with hooks, complex forms can become much simpler without the use of other form libraries.

But before we get to that, let’s take a look at the hook at hand — useState.

useState

useState, as the name describes, is a hook that allows you to use state in your function. We define it as follows:

const [ someState, updateState ] = useState(initialState)

Let’s break this down:

  • someState: lets you access the current state variable, someState
  • updateState: function that allows you to update the state — whatever you pass into it becomes the new someState
  • initialState: what you want someState to be upon initial render

(If you’re unfamiliar with array destructuring syntax, stop here and read this.)

Now that we understand the basic format of useState and how to call and use it, let’s go back to the example from before.

In this example****, counter**** is the state variable, setCount is the updater function, and 0 is the initial state. We use setCount(counter + 1) to increment the count when the button is pressed, making counter + 1 the new value of counter. Alternatively, if we wanted to use the previous state to update the current state, we could pass in the old state to setCount:

setCount(prevCount => prevCount + 1)

This is a simple example that isn’t reflective of what we’d normally use in an actual application. But let’s take a look at something we’re more likely to use — a simple sign-in form for email and password:

import './App.css';
import React, { useState } from 'react';

const LoginForm = () => {
    const [email, setEmail] = useState('');
    const [password, setPassword] = useState('');

    return (
        const { handleSubmit } = this.props;
        <div className="App">
            <header className="App-header">
                <form onSubmit={handleSubmit}>
                    <input value={ email } onChange={(e) => setEmail(e.target.value) } />
                    <input value={ password } onChange={(e) => setPassword(e.target.value) } />
                    <button type="submit">Submit</button>
                </form>
            </header>
        </div>
    )
}

export default LoginForm;

We have two separate state fields and state updaters. This allows us to create really simple forms without creating a whole JavaScript class.

If we wanted to simplify this further, we could create an object as the state. However, useState replaces the whole state instead of updating the object (as setState would), so we can replicate the usual behavior of setState as shown below:

import './App.css';
import React, { useState } from 'react';

const LoginForm = () => {
    const [login, setLogin] = useState({ email: '', password: '' });

    return (
        const { handleSubmit } = this.props;
        <div className="App">
            <header className="App-header">
                <form onSubmit={handleSubmit}>
                    <input value={ login.email } onChange={(e) => setLogin(prevState => { ...prevState, email: e.target.value }) } />
                    <input value={ login.password } onChange={(e) => setLogin(prevState => { ...prevState, password: e.target.value }) } />
                    <button type="submit">Submit</button>
                </form>
            </header>
        </div>
    )
}

export default LoginForm;

If you have state objects more complex than this, you would either want to break them out into separate states as in the first Login example, or use useReducer (we’ll get to that soon!).

So we’ve got state in hooks. What about component lifecycle methods?

useEffect

useEffect is another hook that handles componentDidUpdate, componentDidMount, and componentWillUnmount all in one call. If you need to fetch data, for example, you could useEffect to do so, as seen below.

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

const HooksExample = () => {
    const [data, setData] = useState();

    useEffect(() => {
        const fetchGithubData = async (name) => {
            const result = await axios(`https://api.github.com/users/${name}/events`)
            setData(result.data)
        }
        fetchGithubData('lsurasani')
    }, [data])

    return (
        <div className="App">
            <header className="App-header">
                {data && (
                    data.map(item => <p>{item.repo.name}</p>)
                )}
            </header>
        </div>
    )
}

export default HooksExample;

Taking a look at useEffect we see:

  • First argument: A function. Inside of it, we fetch our data using an async function and then set data when we get results.
  • Second argument: An array containing data. This defines when the component updates. As I mentioned before, useEffect runs when componentDidMount, componentWillUnmount, __and__componentDidUpdate would normally run. Inside the first argument, we’ve set some state, which would traditionally cause componentDidUpdate to run. As a result, useEffect would run again if we did not have this array. Now, useEffect will run on componentDidMount, componentWillUnmount, and if data was updated, componentDidUpdate. This argument can be empty— you can choose to pass in an empty array. In this case, only componentDidMount and componentWillUnmount will ever fire. But, you do have to specify this argument if you set some state inside of it.
useReducer

For those of you who use Redux, useReducer will probably be familiar. useReducer takes in two arguments — a reducer and an initial state. A reducer is a function that you can define that takes in the current state and an “action”. The action has a type, and the reducer uses a switch statement to determine which block to execute based on the type. When it finds the correct block, it returns the state but with the modifications you define depending on the type. We can pass this reducer into useReducer, and then use this hook like this:

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

You use dispatch to say what action types you want to execute, like this:

dispatch({ type: name})

useReducer is normally used when you have to manage complex states — such as the signup form below.

import React, { useReducer } from 'react';

const reducer = (state, action) => {
    switch (action.type) {
        case 'firstName': {
            return { ...state, firstName: action.value };
            }
        case 'lastName': {
            return { ...state, lastName: action.value };
            }
        case 'email': {
            return { ...state, email: action.value };
            }
        case 'password': {
            return { ...state, password: action.value };
            }
        case 'confirmPassword': {
            return { ...state, confirmPassword: action.value };
            }
        default: {
            return state;
        }
    }
};

function SignupForm() {
    const initialState = {
        firstName: '',
        lastName: '',
        email: '',
        password: '',
        confirmPassword: '',
    }
    const [formElements, dispatch] = useReducer(reducer, initialState);

    return (
        <div className="App">
            <header className="App-header">
                <div>
                    <input placeholder="First Name" value={ formElements.firstName} onChange={(e) => dispatch({ type: firstName, value: e.target.value }) } />
                    <input placeholder="Last Name" value={ formElements.lastName} onChange={(e) => dispatch({ type: lastName, value: e.target.value }) } />
                    <input placeholder="Email" value={ formElements.email} onChange={(e) => dispatch({ type: email, value: e.target.value }) } />
                    <input placeholder="Password" value={ formElements.password} onChange={(e) => dispatch({ type: password, value: e.target.value }) } />
                    <input placeholder="Confirm Password" value={ formElements.confirmPassword} onChange={(e) => dispatch({ type: confirmPassword, value: e.target.value }) } />
                </div>
            </header>
        </div>
    );
}

export default SignupForm;

This hook has a lot of additional applications, including allowing us to specify a few reducers throughout our application and then reusing them for each of our components, changing based on what happens in those components. On a high level, this is similar to Redux’s functionality — so we may be able to avoid using Redux for relatively simpler applications.

Custom Hooks

So we’ve covered 3 basic hooks — let’s look at how to make our own. Remember the example I mentioned earlier with the login form? Here it is again as a reminder:

import './App.css';
import React, { useState } from 'react';

const LoginForm = () => {
    const [email, setEmail] = useState('');
    const [password, setPassword] = useState('');

    return (
        const { handleSubmit } = this.props;
        <div className="App">
            <header className="App-header">
                <form onSubmit={handleSubmit}>
                    <input value={ email } onChange={(e) => setEmail(e.target.value) } />
                    <input value={ password } onChange={(e) => setPassword(e.target.value) } />
                    <button type="submit">Submit</button>
                </form>
            </header>
        </div>
    )
}

export default LoginForm;

We useState for both and define a state variable and an updater function for both of the fields. What if we could simplify this further? Here’s a custom hook for handling any kind of input value changes (note: the convention for naming a custom hooks is: use).

import { useState } from 'react';

export const useInputValue = (initial) => {
    const [value, setValue] = useState(initial)
    return { value, onChange: e => setValue(e.target.value) }
}

We use useState to handle the changes as we did in the previous example, but this time we return the value and an onChange function to update that value. So, the login form can now look like this:

import React from 'react';
import { useInputValue } from './Custom'

const Form = () => {
    const email = useInputValue('')
    const password = useInputValue('')

    return (
        <div className="App">
            <header className="App-header">
                <div>
                    <input type="text" placeholder="Email" {...email} />
                </div>
                <div>
                    <input type="password" placeholder="Password" {...password} />
                </div>
            </header>
        </div>
    );
}

export default Form;

We initialize useInputValue with an empty string for both of our fields, and set the result to the name of the field. We can put this back in the input element so the input element renders the value and onChange functions dynamically.

Now, we’ve made this form even simpler — and our custom hook can be reused wherever we need a form input element!

I think that this is one of the most useful things about hooks — the ability to make your own and allow for this previously stateful logic that was locked inside each component to be taken out and reused, allowing for each component to become simpler.

So we’ve gone over: useState, useEffect, useReducer, and finally, custom hooks. There’s a few basic things that we haven’t gone over just yet — namely, the two general rules to follow with Hooks:

  1. Only call Hooks at the top level Not in loops, nested functions, conditions, etc. This ensures that hooks are always called in the same order after each render. This is important because React relies on the order that Hooks are called to determine which state corresponds to a useState call (if you are using multiple). If one of your hooks is hidden in a loop, nested function, or a conditional, the order can change from render to render, messing up which state corresponds to which useState.
  2. Only call Hooks from React functions or custom hooks — In other words, don’t call Hooks from JavaScript functions.

Hopefully this clears up how and when to use hooks for you! Some additional resources you can take a look at:

If you have any questions/comments, please feel free to ask below!