Introduction to React Hooks | Web & App Development

INTRODUCTION TO REACT HOOKS

3 min Sep 02, 2021 Sandip Singh WEB Views : 2244
React Hooks were introduced by React as a new edition in the 16.8 version. These Hooks let you use React features like state without defining a JavaScript class like the state of a component. These hooks though are not used inside classes. Using hooks results in a cleaner code format. Using hooks makes the code much smaller and this will enable you to make use of larger components. 

React Hooks is much simpler for the use of beginners and it has several elements that make it different and advantageous at the same time. An example where Hooks can be specifically advantageous is during the use of forms. You do not have to use form libraries and your complex forms will be transformed into simpler ones.

Use State

useState is a hook that enables you to make use of state in your function. It can be defined as:
const [ someState, updateState ] = useState(initialState)

It can be broken down as follows:

●    someState: it lets you get access to the current state variable- someState
●    updateState: it lets you update the state. When you pass some function into this, it turns into the new someState
●    initialState: This is what you want your someState to be after the initial rendering process.

Let us take the previous example to get a better understanding.

Here, the state variable is counter, the updater function is setCount, the initial state is 0. setCount (counter+1) is used to increase the count as the button is pressed, where the new counter value will be made counter+1. 

Given below is the code to a simple sign-in form for email and password, that can be used with hooks.

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;

The use of two separate state fields and state updaters allows you to create simpler forms without the use of a JavaScript class. You can make this more simple by creating an object as the state. Since useState replaces the entire state, you can repeat the behavior of setState.

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;

In case there are more complicated state objects, you can make use of useReducer which will come up later on in this article.

 

You may also like: A Beginner’s guide to getting started with React

 

 

Use Effect

 

This is a hook that collectively handles componentDidUpdate, componentDidMount as well as componentWillUnmount. In order to fetch data, you can make use of useEffect as given 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;

Observing useEffect, you mainly notice two things:

●    A function: You are able to fetch data by making use of the async function and then set data to get results.
●    An array containing data: It defines the updating of the components. useEffect normally handles componentDidMount, componentDidUnmount and componentDidUpdate.

In the first point, you can see that the state is set, which would normally cause componentDidUpdate to run. Due to this, without this array, useEffect would have to run again.

 

 

Use Reducer

If you are a user of Redux, you will mostly be familiar with useReducer. This hook mainly takes two arguments. They are a reducer and an initial state. A reducer is a function that you can use to define the current state and the action. The action has a certain type which the reducer uses to determine the block to execute by using a switch statement. When the correct block is determined, the state is returned in the required modifications that are performed based on the type. This reducer can be passed into useReducer, using this hook:

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

This dispatch can be used to determine the types of actions that you want to execute, as follows:

dispatch({ type: name})

useReducer can normally be used to manage complete states like signup forms. The code for this is given 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;

Along with this, the useReducer hook has multiple applications. It also allows you to specify a couple of reducers for your application which you can then reuse for each of the components. It changes on the basis of the changes happening in those components. For comparatively simple applications, the use of Redux may appear redundant due to the functionality of this hook.

 

Also Read: Styling React Components

 

Custom Hooks

The three basic hooks given above helps to work with some form of simple as well as complex coding. But you may also need to customize your hooks in order to alter your code.
In order to make a custom hook, we can make use of the login example once again.

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;

With the help of useState for both, a state variable, as well as an updater function, can be effectively defined for the fields. But you can further simplify this entire process. Given below is a custom hook that you can make use to handle any sort of changes in the input values. (The convention for naming custom hooks is: use<function description>)

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


Just like before, useState is used to handle any changes. But the only difference is, the value is returned and an onChange function is used to update the value. The login form now appears 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;

Here, the useInputValue is initialized with an empty string for both the fields, and the result is set to name them. You can put them back in the input element so that it goes on to render the value and the onChange functions in a dynamic manner. This makes the custom hook much simpler and you can even reuse this hook whenever you need a form input element. 

Bottom Line

So far, you have looked into useState, useEffect, useReducer, and custom hooks. In order to use React hooks, you need to remember the two most important rules: Only use Hooks at the top level and now in loops, conditions, or nested functions. This way, you can make sure that the hooks are called in the same order after each rendering. 

The second rule is not to call hooks from JavaScript functions but from React functions of custom hooks. If you keep these rules in mind, you will be able to use React hooks smoothly and successfully.