HOWTO · React
Implementación paso a paso de la función Expandir/Contraer en React
Lea esta guía para saber cómo permitir a los usuarios expandir/contraer contenido haciendo clic en un botón. Es un desglose paso a paso de un ejemplo práctico.
En esta página
Cuando una página contiene secciones extensas o una lista de publicaciones, permita que los lectores elijan qué quieren ver. En React, un botón puede actualizar el estado, y el renderizado condicional puede usarlo para mostrar u ocultar un contenedor o un elemento individual. La clave es modelar por separado cada opción de visibilidad independiente.
Implementar una función para expandir y contraer en React
Use un valor booleano de estado para una sección, o un objeto con claves basadas en los identificadores de los elementos cuando estos necesiten controles independientes. React vuelve a renderizar el JSX correspondiente después de una actualización del estado. Para una sola sección, el patrón esencial es const [isOpen, setIsOpen] = useState(true), un botón con onClick={() => setIsOpen(open => !open)} y contenido condicional como {isOpen && <section>...</section>}. La función de actualización lee el valor más reciente, y la expresión && no renderiza nada cuando el valor es false.
El siguiente modelo sencillo en JavaScript muestra cómo se asignan valores de visibilidad independientes a los identificadores. Es autónomo y no renderiza una interfaz de React:
Ejecute este modelo de JavaScript. El código y su salida visible permanecen en inglés:
const postIds = ["post1", "post2", "post3"];
const visible = Object.fromEntries(postIds.map((id) => [id, true]));
visible.post2 = !visible.post2;
console.log(postIds.map((id) => `${id}: ${visible[id]}`).join(", "));
post1: true, post2: false, post3: true
Aquí tiene un componente de React que reúne estos controles. En un módulo de la aplicación, importe * as React y useState desde react. Usa createElement, por lo que el componente es código fuente de JavaScript válido y también código de React. La evidencia de ejecución proporcionada corresponde al modelo sencillo en JavaScript, no al renderizado de React; por tanto, este componente es ilustrativo y el modelo ejecutable anterior es independiente. Las etiquetas visibles del componente, como “Show posts”, “Hide posts”, “Show All”, “Hide All”, “Collapse” y “Expand”, permanecen en inglés en el ejemplo.
function PostList({ posts }) {
const postIds = posts.map((post) => post.id);
const [sectionOpen, setSectionOpen] = useState(true);
const [visible, setVisible] = useState(() =>
Object.fromEntries(postIds.map((id) => [id, true]))
);
function togglePost(id) {
setVisible((current) => ({ ...current, [id]: !current[id] }));
}
function setAllVisible(value) {
setVisible(Object.fromEntries(postIds.map((id) => [id, value])));
}
return React.createElement(
"section",
null,
React.createElement(
"button",
{ onClick: () => setSectionOpen((open) => !open) },
sectionOpen ? "Hide posts" : "Show posts"
),
React.createElement(
"button",
{ onClick: () => setAllVisible(true) },
"Show All"
),
React.createElement(
"button",
{ onClick: () => setAllVisible(false) },
"Hide All"
),
sectionOpen &&
React.createElement(
"div",
null,
posts.map((post) =>
React.createElement(
"article",
{ key: post.id },
React.createElement(
"button",
{ onClick: () => togglePost(post.id) },
visible[post.id] ? "Collapse" : "Expand"
),
visible[post.id] &&
React.createElement("p", null, post.text)
)
)
)
);
}
El objeto visible asigna a cada identificador de publicación su propio valor booleano. Al hacer clic en el botón de una publicación, solo cambia esa entrada; al propagar current, se conservan los valores de las demás publicaciones. sectionOpen controla la lista principal por separado. Al cerrarla, se ocultan todos los elementos secundarios renderizados sin restablecer sus valores, de modo que al volver a abrirla reaparecen las publicaciones que estaban visibles. Los botones de acciones masivas asignan el mismo valor booleano a todos los elementos. Si el contenedor principal está cerrado, “Show All” sigue actualizando esos valores, pero el contenido de las publicaciones permanece oculto hasta que se abra el contenedor.
Expandir y contraer todo el contenedor en React
Una sección principal tiene su propio estado de visibilidad. Cambie ese valor con un botón y renderice condicionalmente toda la lista dentro de la sección, por ejemplo {sectionOpen && <div>{/* list */}</div>}. Cuando la sección principal está oculta, no se renderiza ninguno de sus elementos secundarios, aunque los valores de visibilidad guardados en el componente padre sigan siendo true. Al mostrar de nuevo la sección, se renderizan los elementos cuyos valores siguen indicando que están visibles.
Esta distinción depende de dónde se guarda el estado: ocultar la sección principal conserva los valores de visibilidad de cada elemento almacenados en el padre, pero el renderizado condicional desmonta los componentes secundarios y descarta el estado local de esos componentes. En el ejemplo anterior, PostList guarda los valores de visibilidad, por lo que volver a abrir la sección restaura los mismos elementos. Si quiere que al cerrar la sección se restablezca la visibilidad de todos los elementos, actualice también esos valores en el mismo controlador de eventos. De lo contrario, mantenga independientes los controles de la sección principal y de cada elemento. El ejemplo de CodeSandbox es la demostración interactiva del artículo de origen.
Expandir y contraer publicaciones específicas en React
Para cada elemento, use su identificador para consultar y actualizar únicamente su propio valor de visibilidad. Una actualización del estado debe crear un objeto nuevo en lugar de modificar el existente. Una actualización funcional resulta útil cuando el siguiente valor depende del estado anterior: setVisible(current => ({ ...current, [id]: !current[id] })). La propiedad calculada [id] selecciona la entrada del elemento cuyo botón se hizo clic, mientras que la propagación conserva intactas las demás entradas.
Dentro de la lista, un botón puede llamar a setVisible para su elemento, y una condición como {visible[id] && <Post post={post} />} controla el contenido de ese elemento. Si la lista puede cambiar, derive el objeto de visibilidad inicial de los identificadores actuales y tenga en cuenta los identificadores nuevos o eliminados cuando cambien los datos; un objeto fijo con claves escritas a mano solo cubre los elementos que nombra.
Expandir o contraer todas las publicaciones en React
Los controles de acciones masivas asignan el mismo valor booleano a todos los elementos. Derive el objeto de los identificadores para mantener alineados la lista y el estado de visibilidad:
const setAllVisible = (value) => setVisible(Object.fromEntries(postIds.map(id => [id, value])))
Use onClick={() => setAllVisible(false)} para Hide All y onClick={() => setAllVisible(true)} para Show All. Estas etiquetas permanecen en inglés en el ejemplo de código. Los controles actualizan los valores de los elementos; si el contenedor principal está oculto, Show All cambia el estado de los elementos secundarios, pero el contenido permanece oculto hasta que se muestre el contenedor.
Conclusión
Use un valor booleano para una sola sección y una correspondencia entre identificadores y valores booleanos para los elementos independientes. Mantenga claros en el estado la visibilidad de la sección principal, la de cada elemento y las acciones masivas, para que cada botón cambie la parte prevista de la interfaz. Los estilos pueden cambiar la apariencia de los botones, mientras que el estado y el renderizado condicional determinan lo que pueden ver los lectores.