Guide to using React Custom Hooks
Published: 2023-05-08 00:00:00

---
title: "Guide to using React Custom Hooks"
date: "2023-05-08"
published: true
intro: "One of the ways you can seperate your app logic from UI is throught the use of custom hook."
---

Many a times in your application, you may need to perform an operation in different components. Of course you can write a function for this purpose and then import such function in the respective components to achieve its intended purpose. What if the operation requires to call one or more React hooks, then the function cannot be used to perform such operations as React hooks can only be called in React functions. To solve this, we make use of React custom hooks. In this blog post, we will dive into what React custom hooks are and how to use them to encapsulate any resuable logic that you may need to share across multiple components.

## What are React Custom Hooks

React Custom Hooks are JavaScript functions that use React hooks to provide a set of reusable functionality that can be shared among different React components. Custom Hooks are named with the `use` prefix to signal that they are intended to be used with hooks. As a convention, the file where a custom hook is written is usually named with the `use` prefix for example `useMediaQuery.js`. But it is required that the name of a function intended to be used as a React custom hook be started with `use`.

It is important to note that custom hooks are React hooks we write ourselves and hence their usage must conform to using in-built React hooks. Rules for using React (custom and in-built) hooks are:

- Only call hooks at the top level components and before any early returns
- Only call hooks from React functions which include React functional components and hooks (custom and in-built)

## Why use React Custom Hooks?

React Custom Hooks provide several benefits which include the following:

1. Encapsulation: Custom Hooks allow you to encapsulate complex logic and state management in a single location which can then be shared among different components in your project. This makes your code more modular and easier to maintain.
2. Reusability: Custom Hooks can be used in multiple components, making it easier to reuse code and reduce duplication.
3. Testability: By encapsulating complex logic in Custom Hooks, it becomes easier to write tests for your code, since you can test the logic in isolation from the components that use it.
4. Code organization and seperation of concerns: Custom Hooks allow you to separate concerns (views from logic) and organize your code into smaller, more manageable pieces.

## Examples of React Custom Hooks

Let's dive into two scenarios where React custom hooks can be used.

Our first example will be `useMediaQuery`. Let's say you have a component in which you want to hide/display some elements or perform any other operation at certain sreen sizes, then you will have to keep track of changes in width of user's device. Here you will need a state variable to get the current screen size as the screen changes and `resize` event listener to be attached to the window.

The component may be written as shown:

```js
import { useState, useEffect } from "react"

const MyComponent = () => {
    const [screenWidth, setScreenWidth] = useState(0)

    useEffect(() => {
        const getScreenWidth = () => {
            setScreenWidth(window.innerWidth)
        }
        getScreenWidth()
        window.addEventListener('resize', getScreenWidth)
        return () => {
            window.removeEventListener('resize', getScreenWidth)
        }
    }, [])

    return (
        <div className='container'>
            Current screen width: {screenWidth}
            {screenWidth > 768 && <p>Show only on screen greater than 768px</p> }
            <p>Show on all screen size</p>
        </div>
    )
}
```

The above implementation will work well. What if you have more of such components in your application where you may need to hide or display some content or perform any other opearation depending on the width of the screen at any instance of time.

What do you do? Would you create a function for getting the screen width every time the window is resized? Remember this involves a state value and you can only declare/call `useState` and any other React hooks such as `useEffect`,  `useRef` etc at top level in a React functional component or hook (in-built and custom).

We can isolate the logic of getting the current size (width) of screen at any time as the window is resized with a custom hook as shown below:

```js
import { useState, useEffect } from "react"

const useMediaQuery = () => {
    const [screenWidth, setScreenWidth] = useState()

    useEffect(() => {
        const getScreenWidth = () => {
            setScreenWidth(window.innerWidth)
        }
        getScreenWidth()
        window.addEventListener('resize', getScreenWidth)
        return () => {
            window.removeEventListener('resize', getScreenWidth)
        }
    }, [])

    return screenWidth
}

export default useMediaQuery
```

You can then import this custom hook in as many components as required in your project to get the current screen size at any time.

Then our `MyComponent` can be refactored as shown below

```js
const MyComponent = () => {
    // make sure you import useMediaQuery as necessary
    const screenWidth = useMediaQuery()

    return (
        <div>
            {screenWidth}
            {screenWidth > 768 && <p>Show only on screen greater than 768px</p> }
            <p>Show on all screen size</p>
        </div>
    )
}
```

Make sure you import the `useMediaQuery` as necessary into any components it is required. For example if `useMediaQuery` is written in a file named `useMediaQuery.js` inside a folder named `/src/hooks` and exported as default from the file as shown below:

```js
// /src/hooks/useMediaQuery.js
import { useState, useEffect } from "react"

const useMediaQuery = () => {
    const [screenWidth, setScreenWidth] = useState()

    useEffect(() => {
        const getScreenWidth = () => {
            setScreenWidth(window.innerWidth)
        }
        getScreenWidth()
        window.addEventListener('resize', getScreenWidth)
        return () => {
            window.removeEventListener('resize', getScreenWidth)
        }
    }, [])

    return screenWidth
}

export default useMediaQuery
```

Then inside `/src/components/MyComponent.js` it will be imported as used as shown below

```js
// /src/components/MyComponent.js
import useMediaQuery from '../useMediaQuery'

const MyComponent = () => {
    const screenWidth = useMediaQuery()
    return (
        <div className='container'>
            Current screen width: {screenWidth}
            {screenWidth > 768 && <p>Show only on screen greater than 768px</p> }
            <p>Show on all screen size</p>
        </div>
    )
}
```

Our second custom hook will be the `useFetch` hook that can be used across multiple components for data fetching. The hook may be written as follows:

```js
// /src/hooks/useFetch.js
import { useState, useEffect } from 'react'

const useFetch = (url) => {
    const [loading, setLoading] = useState(false)
    const [error, setError] = useState(null)
    const [data, setData] = useState(null)
    useEffect(() => {
        // set loading to true to indicate the request is pending
        setLoading(true)
        fetch(url)
        .then(response => {
            // check if the request is successful
            if (!response?.ok) {
                throw new Error('an error occurred')
            }
            return response.json()
        })
        .then(responseData => {
            setData(responseData)
            setLoading(false)
            setError(null)
        })
        .catch(error => {
            setLoading(false)
            setError(error)
        })
    }, [url])
  return {
    loading,
    data,
    error
  }
}

export default useFetch
```

Here, three state variables and the corresponding functions to update their values are initiated. The `useFetch` hook receives a parameter, which is the endpoint to fetch the required data. Depending on your use case, any custom hook can be defined to receive any number of paramters to achieve an intended purpose.

- `loading` and `setLoading` : `loading` indicates if the request has been commenced and pending. Its initial value is set to `false`. `setLoading` is used to update its value as appropriate.
- `error` and `setError` : `error` this is to indicate if the request is rejected or failed and `setError` to update its value as appropriate
- `data` and `setData` : `data` is to be updated to the resource returned from the request. The `setData` is to achieve this purpose.

After initializing the state variables required, the React `useEffect` is use to implement the logic required for fectching the data and updating the variables values.

The value of `loading` is first updated to `true` to indicate the request has been commenced and is pending. Then we made the request to the endpoint, the value of which will be the value of the required `url` parameter.
The response status is checked if the request is fulfilled, that is succesful. Upon success we update the state variables as shown in the code and also upon failed request.

Note any other external library such as [axios](https://axios-http.com/docs/intro) can be used instead of the in-built `fetch` API

Now let's use our custom `useFetch` hook to fetch blog post from a dummy endpoint.

Let's make a seperate component where, `useFetch` hook will be called as shown below:

```js
// src/components/Posts.js
import useFetch from '../hooks/useFetch'

const Posts = () => {
    const {
        loading,
        error,
        data
    } = useFetch('https://jsonplaceholder.typicode.com/posts/')
    if (loading) {
        return <p>loading...</p>
    }
    if (error) {
        return <p>Unable to fetch data</p>
    }
    // check if returned data is truthy.
    // You may want to check for other thing depending on the type and structure of the expected data
    if (data) {
        return (
            data.map((post) => {
                return (
                    <article key={post.id}>
                        <h3>{post.title}</h3>
                        <p>{post.body}</p>
                    </article>
                )
            })
        )
    }
}
```

Here, the `useFetch` custom is imported and called in the `Posts` component. The returned object is destrutured to obtain the `loading`, `error` and `data` state values after destructuring.
We check the values of these in order to render appropriate components/elements.

## Conclusion

We have looked into how React Custom Hooks can make our code conforms to _DRY_ (Don't Repeat Yourself) principle by enabling our code to be modular and reusable. You thoughts and comments are highly welcomed. Thank you!!!