Depuis la version 16.8 de React, on peut utiliser des "hooks" pour bénéficier d'un état local dans des composants "function".

https://fr.reactjs.org/docs/hooks-state.html

Webinar sur les hooks :

 

useState

Référence : https://react.dev/reference/react/useState

UseState est une fonction qui renvoie un tableau :

  • Le premier élément est une variable d'état.
  • Le deuxième élément est une fonction qui permet de modifier cette variable.

Les variables d’état permettent de rafraîchir le rendu de la page afin d’afficher les modifcations.

Pour pourvoir l’utiliser, il faut d’abord l’importer.

import { useState } from "react";

 

Ensuite, il faut le déclarer les variables qui seront respectivement des référence vers la variable d'état et vers la fonction qui permet de la modifier :

const [maVariable, setMaVariable] = useState(valeurInitial);

Ici maVariable et setMaVariable peuvent prendre n’importe quel nom grâce à la décomposition (destructuring) positionnelle.

maVariable est la variable qui va s'afficher.

SetMaVariable permet de modifier MaVariable.

ATTENTION ! Il ne faut pas modifier directement la valeur de MaVariable. On est obligé de passer par une fonction.

Exemple:

import { useState } from "react";

function App() {
  const [counter, setCounter] = useState(0); // Je déclare ma variable d’état et la fonction pour la modifier.

  return (
    <div>
      <p>{counter}</p> // J’affiche la variable counter dans un paragraphe.
      <button onClick={() => setCounter(counter + 1)}>Increment</button> // à chaque clique, j’incémente de 1 la variable counter et le rendu de la page se rafraîchit.
    </div>
  );
}

 

useContext

Utilisé de pair avec useState, useContext permet de partager plus facilement les variables d'états et leurs fonctions (state dans la suite du document)  dans des composants profondément imbriqués.

Le state étant dans le plus haut ancêtre commun dans la pile des composants qui le requièrent, il fallait le passer à travers les props des divers composants afin d'atteindre le composant qui en a réellement besoin.

Exemple :

function Component1() {
  const [user, setUser] = useState("Jesse Hall");

  return (
    <>
      <h1>{`Hello ${user}!`}</h1>
      <Component2 user={user} />
    </>
  );
}

function Component2({ user }) {
  return (
    <>
      <h1>Component 2</h1>
      <Component3 user={user} />
    </>
  );
}

function Component3({ user }) {
  return (
    <>
      <h1>Component 3</h1>
      <Component4 user={user} />
    </>
  );
}

function Component4({ user }) {
  return (
    <>
      <h1>Component 4</h1>
      <h2>{`Hello ${user} again!`}</h2>
    </>
  );
}

Dans cette exemple, nous voyons que le state est partagé dans les composants 2 et 3 mais n'est pas utilisé.

La solution est d'utiliser le contexte.

 

Pour cela, il faut d'abord l'importer et le créer.

import { useState, createContext } from "react";

const UserContext = createContext()

Ensuite, il faut enrober les descendants dans le founisseur de contexte (context.provider) et attribuer au fournisseur la valeur à partager dans les descenants.

function Component1() {
  const [user, setUser] = useState("Jesse Hall");

  return (
    <UserContext.Provider value={user}>
      <h1>{`Hello ${user}!`}</h1>
      <Component2 user={user} />
    </UserContext.Provider>
  );
}

Afin d'utiliser le contexte, nous importons useContext et l'appellons avec comme argument la variable instancié à la création du contexte.

import { useContext } from "react";

const user = useContext(UserContext);

Exemple complet :

import { useState, createContext, useContext } from "react";

const UserContext = createContext(); //Création du contexte

function Component1() {
  const [user, setUser] = useState("Jesse Hall");

  return (
    <UserContext.Provider value={user}> //Enrobage des descendants
      <h1>{`Hello ${user}!`}</h1>
      <Component2 />
    </UserContext.Provider>
  );
}

function Component2() {
  return (
    <>
      <h1>Component 2</h1>
      <Component3 />
    </>
  );
}

function Component3() {
  return (
    <>
      <h1>Component 3</h1>
      <Component4 />
    </>
  );
}

function Component4() {
  const user = useContext(UserContext); //Utilisation du contexte

  return (
    <>
      <h1>Component 4</h1>
      <h2>{`Hello ${user} again!`}</h2>
    </>
  );
}

Dans cette exemple, nous voyons que nous ne passons plus user dans les composants 2 et 3

 

useEffect

useEffect permet d'exécuter des effets secondaires dans vos composants.

Quelques exemples d'effets secondaires : récupération de données, mise à jour directe du DOM et minuteurs (setTimeout).

 

useEffect peut être appeler de 3 manières différentes.

  1. useEffect(() => {
      //S'exécute à chaque rendu.
    });
  2. useEffect(() => {
      //Imite ComponentDidMount et ne s'exécute qu'au premier rendu.
    }, []);
  3. useEffect(() => {
      //S'exécute au premier rendu
    //Imite ComponentDidUpdate et s'exécute à chaque mise à jour de la liste de dépendances.
    }, [deps]);

 

Exemples:

  1. import { useEffect } from 'react';
    function Salutation({ name }) {
      const message = `Bonjour, ${name}!`;
      useEffect(() => {
        // Modifie le titre de la page une seule fois
        document.title = 'Page de salutation';
      }, []);
      return <div>{message}</div>;
    }
  2. import { useEffect } from 'react';
    function Salutation({ name }) {
      const message = `Bonjour, ${name}!`;
      useEffect(() => {
        // Modifie le titre à chaque mise à jour de name
        document.title = 'Page de salutation';
      }, [name]);
      return <div>{message}</div>;
    }

 

Utilisation avec un fetch:

import { useEffect, useState } from 'react';
function FetchEmployees() {
  const [employees, setEmployees] = useState([]);
  useEffect(() => {
    async function fetchEmployees() {
      const response = await fetch('/employees');
      const fetchedEmployees = await response.json(response);
      setEmployees(fetchedEmployees);
    }
    fetchEmployees();
  }, []);
  return (
    <div>
      {employees.map(name => <div>{name}</div>)}
    </div>
  );
}

 

useReducer

useReducer est similaire au useState mais il permet de gérer plus facilement des states complexes en séparant la logique du composant.

redux flux

useReducer accepte deux arguments.

[state, dispatch] = useReducer(<reducer>, <initialState>)

state : Variable d'état.

dispatch : Fonction permettant de modifier le state (fait référence à <reducer>).

<reducer> : La fonction comportant toute la logique du state.

<initialState> : La valeur initial du state.

 

La fonction <reducer> prend deux arguments:

function reducer(state, action) { }

state : la valeur du state courant.

action: C'est un objet contenant le type d'action à exécuter et les valeurs à modifier dans le state.

 

Exemple :

const initialTodos = [
  {
    id: 1,
    title: "Todo 1",
    complete: false,
  },
  {
    id: 2,
    title: "Todo 2",
    complete: false,
  },
];

const reducer = (state, action) => {
  switch (action.type) {
    case "COMPLETE":
      return state.map((todo) => {
        if (todo.id === action.id) {
          return { ...todo, complete: !todo.complete };
        } else {
          return todo;
        }
      });
    default:
      return state;
  }
};

function Todos() {
  const [todos, dispatch] = useReducer(reducer, initialTodos);

  const handleComplete = (todo) => {
    dispatch({ type: "COMPLETE", id: todo.id });
  };

  return (
    <>
      {todos.map((todo) => (
        <div key={todo.id}>
          <label>
            <input
              type="checkbox"
              checked={todo.complete}
              onChange={() => handleComplete(todo)}
            />
            {todo.title}
          </label>
        </div>
      ))}
    </>
  );
}

useRef

Référence : https://beta.reactjs.org/reference/react/useRef#manipulating-the-dom-with-a-ref

Hook qui permet de garder une référence à une valeur dont on a pas besoin pour le "rendering" 

Ex 1 :

Fichier App.js

import React, { useRef } from 'react';
import Form from './components/Form';

function App() {
  const inputRef = useRef(null);
  
  function handleCliclP() {
    console.log(`dans handleCliclP `);
    inputRef.current.focus();
    inputRef.current.value = "Hello World";
  }
  return (
    <div className="App">
      <Form inputRef={inputRef} handleCliclP={handleCliclP} />
    </div>
  );
}

export default App;

Fichier Form.jsx

const Form = ({inputRef,handleCliclP}) => {
  return (
    <form action="">
      <label htmlFor="">Test</label>
      <input ref={inputRef} type="text" />
      <p onClick={handleCliclP}>Test pour mettre le focus sur l'input</p>
    </form>
  );
}

export default Form;

Ex 2: 

const ref = useRef()
...
return (
    <Container title="Gestionnaire de tâches">
      <Form ref={ref} onChange={handleOnChange} onSubmit={handleOnSubmit}/>
      <Select onSelect={handleOnSelect}/>
      <List items={items} onCheck={handleOnCheck}/>
    </Container>
  );
...
// ref fait maintenant référence à un input
const Form = forwardRef(({ onChange, onSubmit }, ref) => {
  return (
    <form className="input-group mb-3" onSubmit={onSubmit}>
      <input
        ref={ref}
        type="text"
        className="form-control form-control-lg mx-0"
        placeholder="Add new..."
        style={{ height: "max-content" }}
        onChange={onChange}
        id="task-input"
      />
      <button type="submit" className="btn btn-info">
        Add
      </button>
    </form>
  )
})
... et va pouvoir interagir avec cette valeur sans que cela n'est d'influence sur le state
...
ref.current.value = null;

useMemo

Référence : https://react.dev/reference/react/useMemo

useMemo est un Hook qui vous permet de mettre en cache le résultat d'un calcul entre les rendus. Si le résultat de votre calcul est toujours le même en fonction d'une variable, useMemo va donc mettre en cache le retour du calcul pour chaque valeur de la dite variable. Cela évitera de rappeler un fonction de calcul alors que ce dernier a déjà été fait.

useForm

Référence : https://react-hook-form.com/

API complète pour faciliter le travail des développeurs lors de la création de formulaires.

 

.