useState est un Hook React qui vous laisse ajouter une variable d’état (state, NdT) dans votre composant.

const [state, setState] = useState(initialState);

Référence

useState(initialState)

Appelez useState à la racine de votre composant pour déclarer une variable d’état.

import { useState } from 'react';

function MyComponent() {
const [age, setAge] = useState(28);
const [name, setName] = useState('Taylor');
const [todos, setTodos] = useState(() => createTodos());
// ...

La convention est de nommer les variables d’états de cette manière : [something, setSomething], en utilisant la déstructuration positionnelle.

Voir d’autres exemples ci-dessous.

Paramètres

  • initialState: La valeur initiale de votre état. Ça peut être une valeur de n’importe quel type, mais il existe un comportement spécial pour les fonctions. Cet argument est ignoré après le rendu initial.
    • Si vous passez une fonction dans votre initialState, elle sera traitée comme une fonction d’initialisation (initializer function, NdT). Elle doit être pure, ne doit pas prendre d’argument, et doit retourner une valeur de n’importe quel type. React appellera votre fonction d’initialisation en initialisant le composant, et stockera sa valeur de retour dans votre état initial. Voir un exemple ci-dessous.

Valeur renvoyée

useState retourne un tableau avec exactement deux valeurs :

  1. L’état courant. Pendant le premier rendu, il sera le même que l’initialState que vous avez passé en entrée.
  2. La fonction set. Elle vous permet de mettre à jour l’état avec une valeur différente et de déclencher un nouveau rendu.

Limitations

  • useState est un Hook, vous ne pouvez donc l’appeler qu’uniquement à la racine de votre composant ou de vos propres Hooks. Vous ne pouvez pas l’appeler à l’intérieur de boucles ou de conditions. Si nécessaire, extrayez un nouveau composant et déplacez l’état dans celui-ci.
  • En mode Strict, React appellera votre fonction d’initialisation deux fois dans le but de vous aider à trouver les impuretés accidentelles. Ce comportement est uniquement présent en mode développement et n’affecte pas la production. Si votre fonction d’initialisation est pure (ce qui devrait être le cas), ça ne devrait pas affecter le comportement. Le résultat d’un des appels sera ignoré.

Les fonctions set, comme setSomething(nextState)

La fonction set retournée par useState permet de mettre à jour l’état avec une valeur différente et de déclencher un nouveau rendu. Vous pouvez directement passer le prochain état, ou une fonction qui le calcule à l’aide de l’état précédent :

const [name, setName] = useState('Edward');

function handleClick() {
setName('Taylor');
setAge(a => a + 1);
// ...

Paramètres

  • nextState: La valeur désirée de l’état. Elle peut être une valeur de n’importe quel type, mais il existe un comportement spécial pour les fonctions.
    • Si vous passez une fonction en tant que nextState, elle sera traitée comme une fonction de mise à jour (updater function, NdT). Elle doit être pure, doit prendre l’état en attente comme seul argument, et doit retourner le prochain état. React mettra votre fonction de mise à jour dans une file et fera un nouveau rendu de votre composant. Pendant le prochain rendu, React va calculer le prochain état en appliquant toutes les fonctions de mises à jour à l’état précédent. Voir un exemple ci-dessous.

Valeur renvoyée

Les fonctions set n’ont pas de valeur de retour.

Limitations

  • La fonction set ne met à jour que les variables d’état pour le prochain rendu. Si vous lisez la variable d’état après avoir appelé la fonction set, vous obtiendrez la même ancienne valeur qui était sur votre écran avant l’appel.

  • Si la nouvelle valeur que vous donnez est identique au state actuel, en comparant au moyen de Object.is, React ne fera pas un nouveau rendu de ce composant et de ses enfants. Il s’agit d’une optimisation. Même si, dans certains cas, React a tout de même besoin d’appeler votre composant sans faire de rendu de ses enfants, ça ne devrait pas affecter votre code.

  • React met à jour les états par lots. Il met à jour l’écran après que tous les gestionnaires d’évènements aient été lancés et qu’ils aient appelés leurs fonctions set. Ça évite des rendu inutiles pour un unique évènement. Dans de rares cas où vous avez besoin de forcer React à mettre à jour l’écran plus tôt, par exemple pour accéder au DOM, vous pouvez utiliser flushSync.

  • Appeler la fonction set pendant le rendu est autorisé seulement dans le composant en train d’être rendu. React ignorera son retour et essayera immédiatement de faire un nouveau rendu avec le nouvel état. Ce modèle est rarement nécessaire, mais vous pouvez l’utiliser pour stocker des informations des précédents rendus. Voir un exemple ci-dessous.

  • En mode Strict, React appellera votre fonction d’initialisation deux fois dans le but de vous aider à trouver les impuretés accidentelles. Ce comportement est uniquement présent en mode développement et n’affecte pas la production. Si votre fonction de mise à jour est pure (ce qui devrait être le cas), ça ne devrait pas affecter le comportement. Le résultat d’un des appels sera ignoré.


Usage

Ajouter un état à un composant

Appelez useState à la racine de votre composant pour déclarer une ou plusieurs variables d’état.

import { useState } from 'react';

function MyComponent() {
const [age, setAge] = useState(42);
const [name, setName] = useState('Taylor');
// ...

La convention est de nommer les variables d’états de cette manière : [something, setSomething], en utilisant la déstructuration positionnelle.

useState retourne un tableau avec exactement deux valeurs :

  1. L’état courant de cette variable d’état, initialement le même que l’état initial que vous avez passé en entrée.
  2. La fonction set qui vous permet de le changer avec n’importe quelle valeur lors d’une interaction.

Pour mettre à jour ce qu’il y a sur l’écran, appelez la fonction set avec le prochain autre état :

function handleClick() {
setName('Robin');
}

React stockera ce prochain état, fera un nouveau rendu de votre composant avec les nouvelles valeurs, et mettra à jour l’interface utilisateur (UI, NdT)

Piège

Appeler la fonction set ne change pas l’état actuel dans le code en train d’être exécuté :

function handleClick() {
setName('Robin');
console.log(name); // Toujours "Taylor" !
}

Elle n’affecte que ce que useState va retourner à partir du prochain rendu.

Exemple basique d'utilisation de useState

Exemple 1 sur 4 ·
Compteur (nombre)

Dans cet exemple, la variable d’état count contient un nombre. Elle est incrémentée en cliquant sur un bouton.

import { useState } from 'react';

export default function Counter() {
  const [count, setCount] = useState(0);

  function handleClick() {
    setCount(count + 1);
  }

  return (
    <button onClick={handleClick}>
      Vous avez cliqué sur ce bouton {count} fois
    </button>
  );
}


Mettre à jour l’état selon son état précédent

Supposons que age vaut 42. Ce gestionnaire appelle setAge(age + 1) trois fois :

function handleClick() {
setAge(age + 1); // setAge(42 + 1)
setAge(age + 1); // setAge(42 + 1)
setAge(age + 1); // setAge(42 + 1)
}

Cependant, après un click, age ne va valoir que 43, plutôt que 45 ! C’est parce qu’appeler la fonction set ne met pas à jour la variable d’état age dans le code en cours d’exécution. Donc, chaque appel à setAge(age + 1) devient setAge(43).

Pour résoudre ce problème, vous devez passer une fonction de mise à jour à setAge au lieu du prochain état :

function handleClick() {
setAge(a => a + 1); // setAge(42 => 43)
setAge(a => a + 1); // setAge(43 => 44)
setAge(a => a + 1); // setAge(44 => 45)
}

Ici, a => a + 1 est votre fonction de mise à jour. Elle prend l’état en attente et calcule à partir de celui-ci le prochain état.

React met vos fonctions de mise à jour dans une file. Ensuite, pendant le prochain rendu, il va les appeler dans le même ordre :

  1. a => a + 1 recevra un état en attente valant 42 et va retourner un prochain état valant 43.
  2. a => a + 1 recevra un état en attente valant 43 et va retourner un prochain état valant 44.
  3. a => a + 1 recevra un état en attente valant 44 et va retourner un prochain état valant 45.

Il n’y a pas d’autres mises à jour en file, React stockera donc à la fin 45 comme étant l’état courant.

Par convention, il est commun de nommer l’argument de l’état en attente selon la première lettre du nom de la variable d’état, comme a pour age. Cependant, vous pouvez également le nommer prevAge, ou quelque chose d’autre que vous trouvez plus clair.

En développement, React pourra appeler deux fois vos mises à jour pour vérifier si elles sont pures.

En détail

Est-il toujours préférable d’utiliser une mise à jour ?

Vous pourrez peut-être entendre des recommandations vous disant de toujours écrire votre code de cette manière, si l’état que vous mettez à jour est calculé depuis l’état précédent : setAge(a => a + 1). Il n’y a aucun mal à le faire, mais ce n’est pas toujours nécessaire.

Dans la plupart des cas, il n’y a aucune différence entre ces deux approches. React vérifiera toujours, pour les actions intentionnelles des utilisateurs, que l’état age soit mis à jour avant le prochain click. Cela signifie qu’il n’y a aucun risque à ce qu’un gestionnaire de click voit un age “obsolète” au début de la gestion des évènements.

Cependant, si vous opérez plusieurs mises à jour dans le même évènement, les mises à jours peuvent être utiles. Elles sont également utiles s’il n’est pas pratique d’accèder à la variable d’état elle-même (vous pourrez rencontrer ce cas en optimisant les rendus).

Si vous préfèrez la consistance plutôt qu’une syntaxe un peu moins verbeuse, il est raisonnable de toujours écrire une mise à jour si l’état que vous mettez à jour est calculé à partir de l’état précédent. S’il est calculé depuis l’état précédent d’une autre variable d’état, vous pourrez peut-être les combiner en une seul objet et utiliser un reducer.

The difference between passing an updater and passing the next state directly

Exemple 1 sur 2 ·
Passing the updater function

This example passes the updater function, so the “+3” button works.

import { useState } from 'react';

export default function Counter() {
  const [age, setAge] = useState(42);

  function increment() {
    setAge(a => a + 1);
  }

  return (
    <>
      <h1>Your age: {age}</h1>
      <button onClick={() => {
        increment();
        increment();
        increment();
      }}>+3</button>
      <button onClick={() => {
        increment();
      }}>+1</button>
    </>
  );
}


Updating objects and arrays in state

You can put objects and arrays into state. In React, state is considered read-only, so you should replace it rather than mutate your existing objects. For example, if you have a form object in state, don’t mutate it:

// 🚩 Don't mutate an object in state like this:
form.firstName = 'Taylor';

Instead, replace the whole object by creating a new one:

// ✅ Replace state with a new object
setForm({
...form,
firstName: 'Taylor'
});

Read updating objects in state and updating arrays in state to learn more.

Examples of objects and arrays in state

Exemple 1 sur 4 ·
Form (object)

In this example, the form state variable holds an object. Each input has a change handler that calls setForm with the next state of the entire form. The { ...form } spread syntax ensures that the state object is replaced rather than mutated.

import { useState } from 'react';

export default function Form() {
  const [form, setForm] = useState({
    firstName: 'Barbara',
    lastName: 'Hepworth',
    email: 'bhepworth@sculpture.com',
  });

  return (
    <>
      <label>
        First name:
        <input
          value={form.firstName}
          onChange={e => {
            setForm({
              ...form,
              firstName: e.target.value
            });
          }}
        />
      </label>
      <label>
        Last name:
        <input
          value={form.lastName}
          onChange={e => {
            setForm({
              ...form,
              lastName: e.target.value
            });
          }}
        />
      </label>
      <label>
        Email:
        <input
          value={form.email}
          onChange={e => {
            setForm({
              ...form,
              email: e.target.value
            });
          }}
        />
      </label>
      <p>
        {form.firstName}{' '}
        {form.lastName}{' '}
        ({form.email})
      </p>
    </>
  );
}


Avoiding recreating the initial state

React saves the initial state once and ignores it on the next renders.

function TodoList() {
const [todos, setTodos] = useState(createInitialTodos());
// ...

Although the result of createInitialTodos() is only used for the initial render, you’re still calling this function on every render. This can be wasteful if it’s creating large arrays or performing expensive calculations.

To solve this, you may pass it as an initializer function to useState instead:

function TodoList() {
const [todos, setTodos] = useState(createInitialTodos);
// ...

Notice that you’re passing createInitialTodos, which is the function itself, and not createInitialTodos(), which is the result of calling it. If you pass a function to useState, React will only call it during initialization.

React may call your initializers twice in development to verify that they are pure.

The difference between passing an initializer and passing the initial state directly

Exemple 1 sur 2 ·
Passing the initializer function

This example passes the initializer function, so the createInitialTodos function only runs during initialization. It does not run when component re-renders, such as when you type into the input.

import { useState } from 'react';

function createInitialTodos() {
  const initialTodos = [];
  for (let i = 0; i < 50; i++) {
    initialTodos.push({
      id: i,
      text: 'Item ' + (i + 1)
    });
  }
  return initialTodos;
}

export default function TodoList() {
  const [todos, setTodos] = useState(createInitialTodos);
  const [text, setText] = useState('');

  return (
    <>
      <input
        value={text}
        onChange={e => setText(e.target.value)}
      />
      <button onClick={() => {
        setText('');
        setTodos([{
          id: todos.length,
          text: text
        }, ...todos]);
      }}>Add</button>
      <ul>
        {todos.map(item => (
          <li key={item.id}>
            {item.text}
          </li>
        ))}
      </ul>
    </>
  );
}


Resetting state with a key

You’ll often encounter the key attribute when rendering lists. However, it also serves another purpose.

You can reset a component’s state by passing a different key to a component. In this example, the Reset button changes the version state variable, which we pass as a key to the Form. When the key changes, React re-creates the Form component (and all of its children) from scratch, so its state gets reset.

Read preserving and resetting state to learn more.

import { useState } from 'react';

export default function App() {
  const [version, setVersion] = useState(0);

  function handleReset() {
    setVersion(version + 1);
  }

  return (
    <>
      <button onClick={handleReset}>Reset</button>
      <Form key={version} />
    </>
  );
}

function Form() {
  const [name, setName] = useState('Taylor');

  return (
    <>
      <input
        value={name}
        onChange={e => setName(e.target.value)}
      />
      <p>Hello, {name}.</p>
    </>
  );
}


Storing information from previous renders

Usually, you will update state in event handlers. However, in rare cases you might want to adjust state in response to rendering — for example, you might want to change a state variable when a prop changes.

In most cases, you don’t need this:

In the rare case that none of these apply, there is a pattern you can use to update state based on the values that have been rendered so far, by calling a set function while your component is rendering.

Here’s an example. This CountLabel component displays the count prop passed to it:

export default function CountLabel({ count }) {
return <h1>{count}</h1>
}

Say you want to show whether the counter has increased or decreased since the last change. The count prop doesn’t tell you this — you need to keep track of its previous value. Add the prevCount state variable to track it. Add another state variable called trend to hold whether the count has increased or decreased. Compare prevCount with count, and if they’re not equal, update both prevCount and trend. Now you can show both the current count prop and how it has changed since the last render.

import { useState } from 'react';

export default function CountLabel({ count }) {
  const [prevCount, setPrevCount] = useState(count);
  const [trend, setTrend] = useState(null);
  if (prevCount !== count) {
    setPrevCount(count);
    setTrend(count > prevCount ? 'increasing' : 'decreasing');
  }
  return (
    <>
      <h1>{count}</h1>
      {trend && <p>The count is {trend}</p>}
    </>
  );
}

Note that if you call a set function while rendering, it must be inside a condition like prevCount !== count, and there must be a call like setPrevCount(count) inside of the condition. Otherwise, your component would re-render in a loop until it crashes. Also, you can only update the state of the currently rendering component like this. Calling the set function of another component during rendering is an error. Finally, your set call should still update state without mutation — this doesn’t mean you can break other rules of pure functions.

This pattern can be hard to understand and is usually best avoided. However, it’s better than updating state in an effect. When you call the set function during render, React will re-render that component immediately after your component exits with a return statement, and before rendering the children. This way, children don’t need to render twice. The rest of your component function will still execute (and the result will be thrown away). If your condition is below all the Hook calls, you may add an early return; to restart rendering earlier.


Troubleshooting

I’ve updated the state, but logging gives me the old value

Calling the set function does not change state in the running code:

function handleClick() {
console.log(count); // 0

setCount(count + 1); // Request a re-render with 1
console.log(count); // Still 0!

setTimeout(() => {
console.log(count); // Also 0!
}, 5000);
}

This is because states behaves like a snapshot. Updating state requests another render with the new state value, but does not affect the count JavaScript variable in your already-running event handler.

If you need to use the next state, you can save it in a variable before passing it to the set function:

const nextCount = count + 1;
setCount(nextCount);

console.log(count); // 0
console.log(nextCount); // 1

I’ve updated the state, but the screen doesn’t update

React will ignore your update if the next state is equal to the previous state, as determined by an Object.is comparison. This usually happens when you change an object or an array in state directly:

obj.x = 10; // 🚩 Wrong: mutating existing object
setObj(obj); // 🚩 Doesn't do anything

You mutated an existing obj object and passed it back to setObj, so React ignored the update. To fix this, you need to ensure that you’re always replacing objects and arrays in state instead of mutating them:

// ✅ Correct: creating a new object
setObj({
...obj,
x: 10
});

I’m getting an error: “Too many re-renders”

You might get an error that says: Too many re-renders. React limits the number of renders to prevent an infinite loop. Typically, this means that you’re unconditionally setting state during render, so your component enters a loop: render, set state (which causes a render), render, set state (which causes a render), and so on. Very often, this is caused by a mistake in specifying an event handler:

// 🚩 Wrong: calls the handler during render
return <button onClick={handleClick()}>Click me</button>

// ✅ Correct: passes down the event handler
return <button onClick={handleClick}>Click me</button>

// ✅ Correct: passes down an inline function
return <button onClick={(e) => handleClick(e)}>Click me</button>

If you can’t find the cause of this error, click on the arrow next to the error in the console and look through the JavaScript stack to find the specific set function call responsible for the error.


My initializer or updater function runs twice

In Strict Mode, React will call some of your functions twice instead of once:

function TodoList() {
// This component function will run twice for every render.

const [todos, setTodos] = useState(() => {
// This initializer function will run twice during initialization.
return createTodos();
});

function handleClick() {
setTodos(prevTodos => {
// This updater function will run twice for every click.
return [...prevTodos, createTodo()];
});
}
// ...

This is expected and shouldn’t break your code.

This development-only behavior helps you keep components pure. React uses the result of one of the calls, and ignores the result of the other call. As long as your component, initializer, and updater functions are pure, this shouldn’t affect your logic. However, if they are accidentally impure, this helps you notice the mistakes.

For example, this impure updater function mutates an array in state:

setTodos(prevTodos => {
// 🚩 Mistake: mutating state
prevTodos.push(createTodo());
});

Because React calls your updater function twice, you’ll see the todo was added twice, so you’ll know that there is a mistake. In this example, you can fix the mistake by replacing the array instead of mutating it:

setTodos(prevTodos => {
// ✅ Correct: replacing with new state
return [...prevTodos, createTodo()];
});

Now that this updater function is pure, calling it an extra time doesn’t make a difference in behavior. This is why React calling it twice helps you find mistakes. Only component, initializer, and updater functions need to be pure. Event handlers don’t need to be pure, so React will never call your event handlers twice.

Read keeping components pure to learn more.


I’m trying to set state to a function, but it gets called instead

You can’t put a function into state like this:

const [fn, setFn] = useState(someFunction);

function handleClick() {
setFn(someOtherFunction);
}

Because you’re passing a function, React assumes that someFunction is an initializer function, and that someOtherFunction is an updater function, so it tries to call them and store the result. To actually store a function, you have to put () => before them in both cases. Then React will store the functions you pass.

const [fn, setFn] = useState(() => someFunction);

function handleClick() {
setFn(() => someOtherFunction);
}