Building and using custom React hooks in React applications

Building and using custom React hooks in our application


Hooks are the new trend in React Community and are inevitable for any React developer who prefers functional components to the class base components. Hooks changes the way our React code is written, making our code clean and more readable. In this article, we will learn what is custom hooks? how to create React custom hooks? and how to use this hook in our application?

Hooks

They are typical javascript functions used in functional components and allow us to use state and life cycle methods like componentDidUpdate, componentDidMount, and more in functional components. It plays a major role in code reusability as it allows us to reuse component and state logic across different components.

Custom hooks:

These are normal javascript functions which contain other hooks inside of it alongside with some common stateful logic that can be reused within multiple components. These functions usually begin with the word use.

Before the arrival of custom hooks, were able to handle repeated and redundant stateful logic inside multiple components by relying on Render props and Higher Order Components. But with the custom hooks, we can accomplish this in a much simpler and cleaner way.

Rules of Hooks

They are two rules to follow when using hooks. Fortunately, react provide a linter plugin to enforce these rules automatically:

Rule 1:
Do not call Hooks inside loops, conditions, or nested functions.
Instead, always use Hooks at the top level of your React function. By following this rule, you ensure that Hooks are called in the same order each time a component renders. That’s what allows React to correctly preserve the state of Hooks between multiple useState and useEffect calls.

Rule 2:

Only Call Hooks from React Functions
Don’t call Hooks from regular JavaScript functions. Instead, you can:

  • Call Hooks from React function components.
  • Call Hooks from custom Hooks.

By following this rule, you ensure that all stateful logic in a component is clearly visible from its source code.

Now that we are conversant with these rules, let’s build our custom react hook

Building our React Custom hooks

Before we start building our custom hooks, npm should be our best guide because there is high possibility that someone has already published it on npm.

Let’s build our first custom hook. First, we need to create an app using create react command as follows:

 npx create-react-app custom-hooks

Next, run the following command:

cd custom-hooks
npm start

useOrderCount hook

Navigate to the src folder and create a hooks folder containing a file useOrderCount.js to write our custom hook logic as follow:

import { useState } from 'react';
function useOrderCountHook() {
   const [orderCount, setOrderCount] = useState(0);

   const incrementOrderCount = () => {
      setOrderCount( orderCount + 1 ) 
    }

    const decrementOrderCount = () => {
      setOrderCount( orderCount - 1 ) 
    }
   return { orderCount, incrementOrderCount, decrementOrderCount };
}
export default useOrderCountHook;

In the code snippet above, we imported the default react hook useState to create a state orderCount with an initial value of 0. Next we created our hook useOrderCountHook which is just a normal javascript function containing the incrementOrderCount and decrementOrderCount function to mutate the orderCount
Now in the above code, we have built our first custom hook orderCountHook and it is ready for use.

Using the **useOrderCountHook** in other components:

In the src folder, create a component folder with the file DisplayCount.js as follows:

import React from 'react';
import useOrderCountHook from '../hooks/useOrderCount';
function DisplayCount() {
   const orderHook = useOrderCountHook();
   return (
     <div>
       <h1>count:{orderHook.orderCount}</h1>
       <button type='button' onClick
         {orderHook.incrementOrderCount}>Increment</button>
        <button type='button' onClick
         {orderHook.decrementOrderCount}>Decrement</button>
    </div>
   );
}
export default DisplayCount;

Update the app.js component as follows:

import React from 'react';
import DisplayCount from './components/useOrderCount';
import './App.css';
function App() {
   return (
     <div>
       <DisplayCount/>
    </div>
   );
}
export default App;

Running the app on the browser we should be able to increment and decrement the order count.

useLocalStorageState hook

our custom hook **useLocalStorageState** will store the user’s age in local storage, update and gets it value from the localStorage.

Navigate to the hooks folder and create useLocalStorageState.js file with the following

import React, { useState} from 'react'
function useLocalStorageState(key, defaultValue) {
    const [state, setState] = useState(() => {
        let value;
        try {
            value = JSON.parse(
                window.localStorage.getItem(key) || String(defaultValue)
            );
        } catch (e) {
            value = defaultValue;
        }
        return value;
    });
    useEffect(
        () => {
            window.localStorage.setItem(key, state);
        }, [state]
    )
    return [state, setState];
}
export default useLocalStorageState;

Our useLocalStorageState hook recieves two parameters (key, defaultValue). It uses the try block to retrieve the value of the key and assigns it to the variable value and catch block assigns the defaultValue to the value variable if an error occurs. The useEffect hook sets, and update the key with the value of state the parameters changes.

Using the useLocalStorageState hook in other components:

In the component folder create DisplayLocalStorageData.js with the following:

import React, { useState} from 'react'
import useLocalStorageState from '../hooks/useLocalStorageState'
function DisplayLocalStorageData () {
    const [userAge, setUserAge] = useLocalStorageState("age",20);
    return (
        <div>
            <h1>{userAge}</h1>
    <button onClick={() => setUserAge(userAge + 1)}>Increment</button>
    <button onClick={() => setUserAge(userAge - 1)}>Decrement</button>
        </div>
    )
}
export default DisplayLocalStorageData;

In order to use the useLocalStorageState we just need to call it with the key and a default value. And the key to localStorage is added with the default value and fetched by our hook while the count is updated by the component itself. all the piece of code that interact with the localstorage is in our useLocalStorageState hook while the state updating part is in the DisplayLocalStorageData component.

Another great advantage that we have here is efficient code reusability across multiple components. Because we can reuse this same piece of code ( useLocalStorageState hook) in any other component that needs to store it state in the local storage.

Creating global state Hooks

Most times when we build web applications, we often access the global state.
In a typical blog, some components need the comments state while some need the posts state. To abstract this functionality, we can create custom Hooks to get certain parts of the state:

First we take advantage of the context API to create a global state object that can be accessible from any component irrespective of how deeply nested the component might be.

// contexts.js
import React from 'react'
export const StateContext = React.createContext({
  state: {
    comments: [],
    posts: []
  },
})

Next, we create a useCommentState hook that will return all the comments from the global state

// useCommentState.js
import { useContext } from 'react'
import { StateContext } from './contexts'
export default function useCommentState () {
  const { state } = useContext(StateContext)
  return state.comments
}

Similarly, we can create a usePostState hook that will return all the posts from the global state.

// usePostsState.js
import { useContext } from 'react'
import { StateContext } from './contexts'
export default function usePostsState () {
  const { state } = useContext(StateContext)
  return state.posts
}

Using the useCommentState hook in other components:

import React, { useState} from 'react'
import useCommentState from './useCommentState'
function DisplayComments () {
    const comments = useCommentState();
    return (
        <div>
            {comments.length > 0 ? (
              comments.map( (comment, idx) => (<h4 key={idx}>{comment}</h4>
              ) : ( <h4>No comment</h4> )
            }
        </div>
    )
}
export default DisplayComments;

Conclusion

In this article, we have learned how to make reusable piece of codes and also share it across multiple components by building our custom react hooks and using it in other components. This makes our codebase more readable and clean. For more information on react hooks, visit the official react documentation.


Share on social media

//