August 07, 2019

Introducing React Hooks

by Ahmed Bouchefra

Introducing React Hooks

React Hooks are a new feature officially introduced in React 16.8 that allows developers to use React concepts like state, context, refs, and lifecycle events without classes which were previously only available in class-based components.

In this article, we'll learn about React Hooks and how they can be used to write React code in the way JavaScript code was originally meant to be written - i.e. in a functional (procedural) approach.

Note: Classes are a recent addition to JavaScript which was introduced in ES 2015. They are essentially syntactical sugar over the prototype-based inheritance.

What is a Hook?

So, first things first - what's a hook in React?

A hook is an API that allows you to hook into React state and other features in your functional components which means you don't need to write a class just for the sake of accessing state or a life-cycle event. This also means you don't need to re-write your functional component into a class component once you realize that you need to access those features.

React provides a set of built-in hooks for accessing common features but you can also create custom hooks. In the following sections, we will see how.

Note: In the following examples, we assume you have a <div> element with a root ID in your index.html file and that you have included the React library.

You can see the example live on this link.

How to Use the useState() Hook

After defining Hooks, let's now start with a first example of how to access the state using the useState() hook.

Thanks to the useState() API, you'll be able to create a new state variable, and it will also provide you with the mechanism to update the value of the variable.

useState() can have an initial value of the state variable and will return an Array that contains both the variable holding the state and a function that you can call to change the state afterward.

First, you need to import the useState() hook:

import { useState } from 'react'

Next, let's create the following functional component:

const App = () => {

    const [name, setName] = useState('React');
    
    return (
      <div>
        Hello {name}
         
        <button onClick={
          () => name === 'React'? setName('Hooks'): setName('React')
          }
        >
        Change State
        </button>
      </div>
    );
}

This component will render the Hello React string with a Change State button. When you click the button, you'll switch between the Hello React and Hello Hooks strings.

Since the useState() hook returns an Array, we used destructuring to access the name variable and the setName() method individually.

The useState() hook takes the 'React' string as an initial value which will be the initial value of the name state. However, we can change this value as we need using the accompanying setName() method.

You can render this component by importing (from the react-dom package) and calling the render() method as follows:

render(<App />, document.getElementById('root'));

How to Use the useEffect() Hook

After learning about the useState() hook, let's see how we can use the useEffect() hook for performing side effects in your functional components - such as fetching data from a remote server or manually changing the DOM.

Using the useEffect() Hook tells React that your function component needs to do something (can be referred to as an "effect") after render. As such, the effect will be performed after the DOM updates.

Let's change the previous example to perform a side effect in our component.

First, you need to import the API:

import  { useEffect }  from  'react';

Next, in the App2() function (which is a copy of the App() component), add the effect as follows:

const App2 = () => {

    const [name, setName] = useState('React');
    
    useEffect(() => {
      document.title = `Hello ${name}`;
    });
    
    return (
      <div>
        Hello {name}
         
        <button onClick={
          () => name === 'React'? setName('Hooks'): setName('React')
          }
        >
        Change State
        </button>
      </div>
    );

}

The useEffect() takes a function that will be executed by React when the rendering of the component is finished. In this example, we simply set the document title to the Hello React string and it will be changed to Hello Hooks when you click on the Change State button.

Note: According to the docs, the useEffect() hook is equivalent to calling the React class lifecycle methods combined i.e componentDidMount, componentDidUpdate, and componentWillUnmount.

Protect your React App with Jscrambler

How to use the useContext() Hook

The useContext() hook makes it easy to consume the context in your functional components.

Let's see an example for accessing the context using this hook.

First, you need to import the createContext() and useContext() APIs:

import  { createContex, useContext }  from  'react';

Next, create a context:

const NameContext = createContext();

Next, add a HelloReact component as follows:

const HelloReact = () => {

  const name = useContext(NameContext);

  return (
      <p>
        Hello {name}
      </p>
  );
};

This component expects a name context to be provided from the parent component.

Next, in the App4() function, call the HelloReact component and provide the context:

const App4 = () => {

  return (
    <NameContext.Provider value ={ 'React' }>
      <HelloReact />
    </NameContext.Provider>);

}

You’ll see Hello React in your screen!

How to Use the useReducer() Hook

According to the docs, useReducer() is:

An alternative to useState. Accepts a reducer of type (state, action) => newState, and returns the current state paired with a dispatch method. (If you’re familiar with Redux, you already know how this works.)

So, if you need to manage a relatively more complex state in your component or when the next state depends on the previous one, you can use useReducer() instead of useState().

useReducer() takes a reducer with an initial state, returns the current state and dispatches a function.

A reducer is a concept that became common among the JavaScript community with the extensive use of the Redux state management, which basically makes it easy to manage complex states in JavaScript apps. But, in fact, this concept is not new and doesn't necessarily relate to Redux. It's simply a function that takes a set of values and returns one value by executing some logic or algorithm over the set of values that are fed as input. JavaScript has a .reduce() method that takes a reducer function and outputs a single value.

You can read this article for more details about reducers.

This is how useReducer() can be used:

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

Let's change our App() component to use useReducer():

const App3 = () => {

  const initialState = { name: 'React' };

  function reducer(state, action) {
    switch (action.type) {
      case 'react':
        return { name: 'React' };
      case 'hooks':
        return { name: 'Hooks' };
      default:
        throw new Error();
    }
  }

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


  useEffect(() => {
    document.title = `Hello ${state.name}`;
  });

  return (
    <div>
      Hello {state.name}

      <button onClick={
        () => state.name === 'React' ? dispatch({type: 'hooks'}) : dispatch({type: 'react'})
      }
      >
        Change State
      </button>

    </div>
  );

}

Hooks Rules

React Hooks are useful if you want to write function-based components with all the features available in classes - but, according to the docs, they have the following rules:

  • Only call hooks at the top level;
  • Don't call Hooks inside loops, conditions, or nested functions;
  • Don't call Hooks from regular JavaScript functions but from React functional components.

How to Create a Custom Hook?

While React provides built-in hooks for the most commonly used features, sometimes you'll find yourself needing a custom hook. This is possible and easily accomplished with React Hooks, particularly for logic that's repeated throughout multiple components.

A custom Hook is simply a JavaScript function whose name starts with use and which can call other Hooks.

In our various components, we used the useEffect() hook to change the document title. While this logic is not complex, let's extract that into a custom hook just for the sake of our example:

function useDocumentTitle(title) {
  useEffect(() => {
    document.title = `Hello ${title}`;
  });
}

Now, you can replace that logic in the App(), App2() and App3() components with:

useDocumentTitle(name); // or useDocumentTitle(state.name) in App2()

You can think of a custom hook as an alternative to the common ways of reusing stateful logic between components such as render props and higher-order components.

According to the docs, it's particularly a convention and not a feature that you can use to share reusable code which has some differences vs. using components:

Unlike a React component, a custom Hook doesn’t need to have a specific signature. We can decide what it takes as arguments and what, if anything, it should return. In other words, it’s just like a normal function. Its name should always start with use so that you can tell at a glance that the rules of Hooks apply to it.

Conclusion

In this post, we introduced React Hooks to beginners with examples for common built-in hooks like useState() for accessing the state, useEffect() for performing side effects, useReducer() for complex state management in functional components.

You can see additional Hooks in the React docs.

Lastly, don't forget that if you're building React applications with sensitive logic, you should always protect them against abuse, tampering and reverse-engineering by following our guide.