# React Hooks Personnalisés : useArray

Nouvelle semaine, nouveau Hook à ajouter à notre collection. Dans cet épisode, nous allons implémenter le Hook useArray pour simplifier la manipulation des tableaux en React. Vous êtes prêts ? C'est parti ! 😎

## Motivation

Comme à notre habitude, commençons par découvrir comment ce Hook pourra nous être utile. Pour cela, soyons
originaux et prenons l'exemple d'une application To-Do list en React. Nous aurons besoin de gérer
les tâches que l'utilisateur va ajouter et supprimer. Nous allons donc utiliser un tableau, et ce à
l'aide du Hook `useState`. La fonction `addTask` pourrait ressembler à ceci :


```jsx
const addTask = (newTask) => {
  setTasks(oldTasks => [...oldTasks, newTasks])
}
```

Ensuite, nous aurons besoin d'une fonction `removeTask`, qui pourrait quant à elle ressembler à cela :

```jsx
const removeTask = (index) => {
  setTasks(oldTasks => oldTasks.filter((_, i) => i !== index))
}
```

Comme vous pouvez le constater, cela peut très vite devenir difficile à lire. Nous allons donc créer
notre propre Hook `useArray` pour extraire cette logique au sein d'une fonction réutilisable, dans le but de simplifier notre code.


## Implémentation

Tout d'abord, créons le squelette de notre Hook.

```jsx
const useArray = (initialValue = []) => {
  const [value, setValue] = useState(initialValue)

  return { value, setValue }
}
```

Nous allons maintenant y créer une fonction `push` pour ajouter un élément à la fin de notre tableau.

```jsx
const push = element => {
  setValue(oldValue => [...oldValue, element]);
};
```

Ajoutons également la fonction `remove` pour supprimer l'élément à l'indice donné.

```jsx
const remove = index => {
  setValue(oldValue => oldValue.filter((_, i) => i !== index));
};
```

Il pourrait également être utile de disposer d'une fonction `isEmpty` pour vérifier la vacuité du tableau.

```jsx
  const isEmpty = () => value.length === 0;
```

En combinant toutes ces fonctions, nous obtenons notre Hook final.

```jsx
const useArray = (initialValue = []) => {
  const [value, setValue] = useState(initialValue);

  const push = element => {
    setValue(oldValue => [...oldValue, element]);
  };

  const remove = index => {
    setValue(oldValue => oldValue.filter((_, i) => i !== index));
  };

  const isEmpty = () => value.length === 0;

  return { value, setValue, push, remove, isEmpty };
};
```

Si vous manipulez de grandes quantités de données, n'hésitez-pas à optimiser ce Hook en utilisant 
`useCallback` (plus d'informations sur [cet article](https://blogs.infinitesquare.com/posts/web/presentation-du-hook-usecallback)).

> Exemple :
> ```jsx
> const push = useCallback(element => {
>   setValue(oldValue => [...oldValue, element])
> }, [])
> ```

N'hésitez-pas non plus à ajouter d'autres fonctions (comme `map` ou `unshift`) si vous en avez besoin.
De manière générale, n'hésitez-pas à adapter ce Hook en fonction de *vos* besoins, car c'est le vôtre ! 😉


## Utilisation

Revenons à notre exemple de To-Do list. En utilisant notre tout nouveau Hook, voici à quoi pourrait
ressembler notre composant principal :

```jsx
const TodoList = () => {
  const tasks = useArray([]);
  const [newTask, setNewTask] = useState("");

  // "Add" button clicked
  const handleSubmit = e => {
    e.preventDefault();
    tasks.push(newTask);
    setNewTask("");
  };

  const handleInputChange = e => setNewTask(e.target.value);

  return (
    <>
      <h1>Todo List</h1>
      <form onSubmit={handleSubmit}>
        <input type="text" value={newTask} onChange={handleInputChange} />
        <button>Add</button>
      </form>
      {tasks.isEmpty() ? (
        <p>No tasks to display</p>
      ) : (
        <ul>
          {tasks.value.map((task, index) => (
            <li key={index}>
              <input
                type="checkbox"
                onClick={() => tasks.remove(index)}
                checked={false}
              />
              {task}
            </li>
          ))}
        </ul>
      )}
    </>
  );
};
```

À noter que nous n'avons même plus besoin de définir des fonctions `addTask` et `removeTask` : nos méthodes
`tasks.push` et `tasks.remove` sont déjà assez explicites.


## Idées d'Améliorations

Pour aller plus loin, voici quelques idées d'améliorations pour enrichir ce Hook.

- Ajouter une fonction `reverse` pour retourner le tableau
- Ajouter une fonction `sort` pour trier le tableau
- Ajouter une fonction `clear` pour vider le tableau


## Conclusion

Une fois de plus, nous avons vu en quoi les Hooks personnalisés pouvaient être utiles : notre
code final est plus simple, plus propre et toute la logique redondante est extraite dans une fonction
à part. J'espère que ce Hook vous sera utile au sein de vos projets, et je vous dis à bientôt pour un nouveau
Hook personnalisé. 👋

---

Code source disponible sur [CodeSandbox](https://codesandbox.io/s/custom-react-hooks-usearray-fi13z?file=/src/App.js).

