Compartir estado entre componentes

Hay ocasiones en las que quieres que el estado de dos componentes cambien siempre juntos. Para hacerlo, elimina el estado de los dos, muévelo al padre común más cercano y luego pásalo a través de props. Esto se conoce como elevar el estado (lifting state up), y es una de las cosas más comunes que harás al escribir código React.

Aprenderás

  • Cómo compartir el estado entre componentes por elevación
  • Qué son los componentes controlados y no controlados

Elevar el estado con un ejemplo

En este ejemplo, el componente padre Accordion renderiza dos componentes Panel:

  • Accordion
    • Panel
    • Panel

Cada componente Panel tiene un estado booleano ‘isActive’ que determina si su contenido es visible.

Presiona el botón Mostrar en ambos paneles:

import { useState } from 'react';

function Panel({ title, children }) {
  const [isActive, setIsActive] = useState(false);
  return (
    <section className="panel">
      <h3>{title}</h3>
      {isActive ? (
        <p>{children}</p>
      ) : (
        <button onClick={() => setIsActive(true)}>
          Mostrar
        </button>
      )}
    </section>
  );
}

export default function Accordion() {
  return (
    <>
      <h2>Alma Ata, Kazajistán</h2>
      <Panel title="Acerca de">
        Con una población de unos 2 millones de habitantes, Alma Ata es la mayor ciudad de Kazajistán. De 1929 a 1997 fue su capital.
      </Panel>
      <Panel title="Etimología">
        El nombre proviene de <span lang="kk-KZ">алма</span>, palabra en kazajo que significa "manzana" y suele traducirse como "lleno de manzanas". De hecho, se cree que la región que rodea a Alma Ata es el hogar ancestral de la manzana, y se considera que este fruto silvestre <i lang="la">Malus sieversii</i> es un candidato probable para el ancestro de la manzana doméstica moderna.
      </Panel>
    </>
  );
}

Observa que pulsar el botón de un panel no afecta al otro: son independientes.

Diagrama que muestra un árbol de tres componentes, un padre llamado Accordion y dos hijos llamados Panel. Ambos componentes Panel contienen isActive con valor false.
Diagrama que muestra un árbol de tres componentes, un padre llamado Accordion y dos hijos llamados Panel. Ambos componentes Panel contienen isActive con valor false.

Inicialmente, el estado isActive de cada Panel es false, por lo que ambos aparecen colapsados

El mismo diagrama que el anterior, con el valor de isActive establecido en true, mediante un click, en el primer componente hijo Panel. El segundo componente Panel todavía contiene el valor false.
El mismo diagrama que el anterior, con el valor de isActive establecido en true, mediante un click, en el primer componente hijo Panel. El segundo componente Panel todavía contiene el valor false.

Al hacer clic en cualquiera de los botones del Panel sólo se actualizará el estado isActive de ese Panel.

Pero ahora digamos que quieres cambiarlo para que solo se mantenga expandido un panel a la vez. Con ese diseño, al expandir el segundo panel se debería colapsar el primero. ¿Cómo lo harías?

Para coordinar estos dos paneles, es necesario “elevar su estado” a un componente padre en tres pasos:

  1. Remueve el estado de los componentes hijos.
  2. Transfiere los datos codificados desde el padre común.
  3. Añade estado al padre común y pasarlo hacia abajo junto con los controladores de eventos.

Esto permitirá que el componente Accordion coordine ambos Panel y sólo expanda uno a la vez.

Paso 1: Elimina el estado de los componentes hijos

Le darás el control de isActive del Panel a su componente padre. Esto significa que el componente padre pasará isActive al Panel como prop. Empieza por eliminar esta línea del componente Panel:

const [isActive, setIsActive] = useState(false);

Y en su lugar, añade isActive a la lista de props del Panel:

function Panel({ title, children, isActive }) {

Ahora el componente padre de Panel puede controlar isActive pasándolo como prop. A la inversa, el componente Panel ahora no tiene ningún control sobre el valor de isActive—¡ahora depende del componente padre!

Paso 2: Pasa los datos codificados desde el componente padre común

Para elevar el estado, debes localizar el componente padre común más cercano de ambos componentes hijos que deseas coordinar:

  • Accordion (padre común más cercano)
    • Panel
    • Panel

En este ejemplo, es el componente Accordion, dado que está por encima de ambos paneles y puede controlar sus props, se convertirá en la “fuente de la verdad” para saber qué panel está actualmente activo. Haz que el componente Accordion pase un valor codificado de isActive (por ejemplo, true) a ambos paneles:

import { useState } from 'react';

export default function Accordion() {
  return