Posted on 10/31/2021
Another week, another custom React hook for your hooks backpack. In this episode, we'll implement the useArray
hook to make arrays management easier. Ready? Let's go!ย ๐
As usual, let's first discover how this hook could be useful to you. Let's be original and creative: suppose you are building a To-Do list application with React. At some point, you'll have to manage the user's tasks: to do so, you'll use an array, along with the useState
hook. The addTask
function might look like this:
1const addTask = (newTask) => {
2 setTasks(oldTasks => [...oldTasks, newTasks])
3}
Then, you would have a removeTask
function, that could look like this:
1const removeTask = (index) => {
2 setTasks(oldTasks => oldTasks.filter((_, i) => i !== index))
3}
As you can see, this can quickly become a bit hard to read. This is why we will create the useArray
hook to simplify our code.
First, let's create the hook's base structure.
1const useArray = (initialValue = []) => {
2 const [value, setValue] = useState(initialValue)
3
4 return { value, setValue }
5}
We will then add the push
function to add an element at the end of the array.
1const push = element => {
2 setValue(oldValue => [...oldValue, element]);
3};
Let's also create the remove
function to remove an element at a given index.
1const remove = index => {
2 setValue(oldValue => oldValue.filter((_, i) => i !== index));
3};
It can also be handy to add an isEmpty
function to check for the array emptiness.
1 const isEmpty = () => value.length === 0;
Combining all these functions together, here's how the final hook will look like:
1const useArray = (initialValue = []) => {
2 const [value, setValue] = useState(initialValue);
3
4 const push = element => {
5 setValue(oldValue => [...oldValue, element]);
6 };
7
8 const remove = index => {
9 setValue(oldValue => oldValue.filter((_, i) => i !== index));
10 };
11
12 const isEmpty = () => value.length === 0;
13
14 return { value, setValue, push, remove, isEmpty };
15};
If you are working with large amount of data, feel free to optimize this hook by using useCallback
(more info here).
Example:
1const push = useCallback(element => {
2 setValue(oldValue => [...oldValue, element])
3}, [])
Also, if you need other array methods such as map
or unshift
, don't hesitate to adapt it to your needs (you can even add custom functions).
Back to our To-Do list example. By using our brand-new hook, this is how the component could now look like:
1const TodoList = () => {
2 const tasks = useArray([]);
3 const [newTask, setNewTask] = useState("");
4
5 // "Add" button clicked
6 const handleSubmit = e => {
7 e.preventDefault();
8 tasks.push(newTask);
9 setNewTask("");
10 };
11
12 const handleInputChange = e => setNewTask(e.target.value);
13
14 return (
15 <>
16 <h1>Todo List</h1>
17 <form onSubmit={handleSubmit}>
18 <input type="text" value={newTask} onChange={handleInputChange} />
19 <button>Add</button>
20 </form>
21 {tasks.isEmpty() ? (
22 <p>No tasks to display</p>
23 ) : (
24 <ul>
25 {tasks.value.map((task, index) => (
26 <li key={index}>
27 <input
28 type="checkbox"
29 onClick={() => tasks.remove(index)}
30 checked={false}
31 />
32 {task}
33 </li>
34 ))}
35 </ul>
36 )}
37 </>
38 );
39};
Notice that we don't even need the addTask
and removeTask
functions anymore, as our tasks.push
and tasks.remove
ones are already explicit and easy to read.
To go further, here are some ideas for improvement to enhance this hook.
reverse
function to reverse the arraysort
function to sort the arrayclear
function to clear the arrayOnce again, we've seen how powerful custom React hooks can be: our code is much simpler, cleaner, and all the redundant logic is moved away in a separate function. I hope this hook will be useful to you for your future (or existing) projects, and see you next time for a new custom hook.ย ๐
Source code available on CodeSandbox.