🪝 Creating a custom React hook
Tags: react, javascript, webdev
Published: 2021-10-31 19:40:05

![Screenshot of some random code](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8nco1hraj1amfx9hqc7r.jpg)
You've heard about React hooks and you start to get a grasp on it, you understand what the main ones do and use them effortlessly in your components.

It's time to level up and to start creating your custom hooks to contain the business logic of your application.

The main benefit of building your own hooks is that you can encapsulate the logic and reuse them across your application, avoiding repeating code in multiple places.

Let's imagine an application that displays 2 set of items to the users: _tasks_ and _projects_. For this you have 2 separate components that call 2 different API endpoints. You need to handle the request lifecycle and keep the state for both of them so let's try to code a solution that would work for each case.

### Creating the hook

The standard practice for hooks in React is that their name starts with `use`, so we'll call our hook `useItemsLoader`.

```jsx
const useItemsLoader = () => {};
```

### Defining the state, input and output

We want to make the hook configurable for different endpoints so we will add an input parameter with this.

Our hook will be responsible for storing the data (with the items) and the state of the request (`LOADING`, `DONE` and `ERROR`). Since the shape of the data is simple enough (just a couple of fields) we'll store it in a single variable. We will use the `useState` hook for this.

Finally, we will return the data so the caller component of the hook can render itself properly.

```jsx
const useItemsLoader = (endpoint) => {
  const [data, setData] = useState({ items: null, state: 'LOADING' });
  return data;
};
```

### Requesting the data

We need a way to trigger the request, so we will use the `useEffect` hook. The hook will fetch the data once the component has been mounted.

We will also manage the lifecycle of the request, setting the state based on the outcome.

```jsx
useEffect(() => {
	fetchItems(endpoint)
    .then( items => setData({ items, state: 'DONE' }))
		.catch( () => setData({ items: null, state: 'ERROR' });
}, [endpoint]);
```

### Putting everything together

This is the final result of the hook:

```jsx
const useItemsLoader = (endpointPath) => {
	const [data, setData] = useState({ items: null, state: 'LOADING' });

	useEffect(() => {
		fetchItems(endpoint)
	    .then( items => setData({ items, state: 'DONE' }))
			.catch( () => setData({ items: null, state: 'ERROR' });
	}, [endpoint]);

	return data;
};
```

And this is how we can use it in out component:

```jsx
const Tasks = () => {
  const tasksData = useItemsLoader('path/to/tasks');

  if (tasksData.state === 'LOADING') return <div>Loading data...</div>;
  if (tasksData.state === 'ERROR') return <div>Something went wrong</div>;

  return (
    <div>
      <h1>Tasks</h1>
      {tasksData.items.map((task) => (
        <Task task={task} />
      ))}
    </div>
  );
};
```

We could do the same with our other `Projects` component, reusing the `useItemsLoader` but with a different endpoint.

Custom hooks are a good solution even for more complex solutions. They allow us to have the logic contained and separated from our components improving the maintainability of our code. If we need to change something in the future we will need to do it in a single place.