Creating custom React hooks for a simple To-do list app
Tags: react, webdev, javascript, tutorial
Published: 2020-04-01 13:53:25

<p>In this post I’m going to do a <strong>refactor</strong> to a simple To-do list app. The goal is to <strong>create some custom hooks</strong> so you can see how simple it is.</p><blockquote>If you don’t know what hooks are and how awesome they are, it’s a good idea to <a href="https://reactjs.org/docs/hooks-intro.html">check the official documentation</a> first</blockquote><h3>But… Why bother in the first place? 😕</h3><p>Coding a custom hook provides us with two very clear benefits. First, we can <strong>extract common functionality to be reused</strong> by several components. We do that with components, so it makes sense to do the same with functions.</p><p>And secondly, we get rid of a lot of boilerplate code in our components, which make them<strong> cleaner and easier to read.</strong></p><p>This two benefits will become clearer when you see the example ✍️</p><h3>Let’s start with a simple To-do list app ✔️</h3><p>I’ve coded a very simple app. It allows the user to:</p><ul><li>Type a to-do item and add it to the list</li><li>See the list</li><li>Mark a to-do as completed/uncompleted by clicking on it</li><li>Removing a to-do from the list by clicking the red <strong>X </strong>button next to it</li></ul><p>See it in action:</p>

{% codesandbox simplereacttodoapp-mn3l5 %}

<p>And here is the code:</p>

{% gist https://gist.github.com/francodalessio/b3a3118a8d150dcfeba1e79a4eff3aab %}

<p>Now that we’re ready, let’s start building some custom hooks! 🚀</p>
<img src="https://media.giphy.com/media/PoGg4p9nOR6yO6JvHW/source.gif" />
<h3>A custom hook to control the input field</h3><p>Let’s start with a simple one. It’s a good idea to create a hook to manage the state of the input field we have. Why? Because it will be <strong>highly reusable</strong>; every other component that has a form with input fields will be able to benefit from it.</p><p>Right now we’re controlling the input field with this piece of state:</p>

<pre>
const [todo, setTodo] = React.useState("");
</pre>

<p>When the value is changed (the user types something) we’re calling this function:</p>

<pre>
const handleChange = e =&gt; {
  setTodo(e.target.value);
};
</pre>

<p>And <strong><em>onSubmit</em></strong>, after the new to-do is added to the list, we’re cleaning the input field:</p><pre>setTodo(&quot;&quot;);</pre><p>In every component that has an input field, we would need to have these three things. <strong>Keep them in mind</strong> because we’re going to use these 3 things in our hook.</p><h4>Creating the new useInput hook</h4><p>First, start by creating a new folder <strong><em>hooks</em></strong> (just to keep things organized) and create a new file called <strong><em>useInput.js.</em></strong></p><blockquote>The name of every official hook start with the word <strong>use</strong>, so follow that rule when creating a custom one</blockquote><p>You’ve seen that React’s <strong>useState</strong> hook returns two things: the value and a function to update it. However, it’s not necessary that a hook returns just 2 things.</p><p>In fact, we’re making <strong>our hook return 3 things</strong>:</p><ul><li>The value</li><li>A function to handle the change (update the value)</li><li>A function to reset the value (clean the input)</li></ul><p>As for the parameters, our hook function <strong>only needs one thing</strong>: the initial value.</p><p>This is the skeleton of our hook:</p>

<pre>
function useInput(initialValue) { 
  // Code goes here
  return [state, handleChange, reset];
}
export default useInput;
</pre>

<p>Now we have to add the body of our function. But actually we’ve already done it! Remember, <strong>we’re just extracting the logic from our component</strong>.</p><p>So we’re going to use the 3 things we enumerated before (and I asked you to keep in mind 😉). The final hook should look like this:</p>

{%gist https://gist.github.com/francodalessio/4510259192e6b393a89bb2acc716fafa %}

<p>So now we can go ahead and a<strong>dd the hook to our component</strong>! 🚀 That includes importing it:</p><pre>import useInput from &#39;./hooks/useInput&#39;;</pre><p>Actually calling it:</p><pre>const [todo, setTodo, resetTodo] = useInput(“”);</pre><p>And also get rid of our <strong>handleChange</strong> function and replace some of the code to use the two functions our hook provides. The component should now look like this:</p>

{%gist https://gist.github.com/francodalessio/119f02deb6a3ded546384bf17622ce73 %}

<p>Better, right? It’s cleaner; not a huge change because our hook is small, but remember that we’ll be able to use this hook in <strong>every other component that has an input field</strong> 🎉</p>

<img src="https://media.giphy.com/media/LqhynjraNMUL6jwkEF/giphy.gif" />

<h3>Let’s build a bigger hook 💪</h3><p>Ok, so now that we’ve seen how to create a custom hook and how easy it is, let’s create a bigger one.</p><p>Our app is really small and doesn’t have a lot of functionality, but it does <strong>share a</strong> <strong>common thing with almost every application</strong>, big or small. Can you guess what?</p><p>Exactly, it uses a <strong><em>list</em></strong><em>. </em>Have you ever coded a React application without using an array list? 🤔</p><p>It would be great if we could create a hook to handle arrays. That would be useful not only for other components in this application, but also for virtually <strong>any other application</strong>.</p><h4>Creating the new useArray hook</h4><p>We have 3 actions involving our to-do list:</p><ul><li>We can add a to-do</li><li>We can remove a to-do</li><li>We can toggle the completed status of a to-do</li></ul><p>Let’s move all that logic to a hook called <strong>useArray. </strong>As this hook is going to return several functions (plus the list itself) and may even grow in the future, I’m not going to do it return an array as the previous hook. That would mean destructuring a lot of things and wouldn’t look nice.</p><p>Instead, I’m going to make our function <strong>return an object</strong>, containing all we need. There’s no rule saying that you must make your hook return an array, so this is not a problem.</p><p>As with the previous hook, let’s begin with the skeleton. We know that we will be receiving the initial value for the list itself and that we’re returning that alongside with the 3 methods we mentioned:</p>

<pre>
function useArray(initialList) {
  const [list, setList] = useState(initialList);
  return {
    list,
    addItem: () => {},
    removeItem: () => {},
    toggleItem: () => {},
  };
}
export default useArray;
</pre>

<p>Now we just need to extract those 3 functions from our component and add them to this new hook, which will end up looking like this:</p>

{%gist https://gist.github.com/francodalessio/9e5128521b4177dfa5589761524c64fe %}

<p>Now for the big moment 🥁 Let’s get rid of all these functions on the component and <strong>replace them with our brand new hook</strong>:</p>

{%gist https://gist.github.com/francodalessio/0eab1d3393cf89603854649a2d17a463 %}

<p>Now that’s <strong>a lot cleaner</strong>, right? 😎 And of course, it <strong>works the same as before</strong>.</p>

<img src="https://media.giphy.com/media/UTEwvH9aKvnlC/source.gif" />

<p>We got rid of a lot of code! Our component is definitely <strong>easier to read</strong> now. And the best part, once again: <strong>we’ll be able to use our hook in any other component</strong> that handles a list like this.</p><h3>So… What do you think? 😃</h3><p>What’s your opinion on custom hooks? Have you ever used it? If you haven’t yet, hopefully you will after reading this!</p><p>I hope it was useful and please forgive me (and let me know 🙏) if there is any error in the code.</p><p>Thanks for reading ❤️</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4842bfea04dc" width="1" height="1">