Tengo un componente simple con algunos bits de marcado anidados:
import React, { ReactElement } from "react"; type MenuItem = { title: string; subItems?: Array<string>; }; type MenuConfig = Array<MenuItem>; function Item({ item }: { item: MenuItem }): ReactElement { const [showItem, setShowItem] = React.useState(false); const handleShowItem = (): void => { setShowItem(!showItem); }; return ( <> {item.subItems && ( <button onClick={() => handleShowItem()}> {showItem ? "Hide" : "Expand"} </button> )} {showItem && <SubItem item={item} />} </> ); } function SubItem({ item }: { item: MenuItem }): ReactElement { const { title } = item; return ( <ul> {item?.subItems?.map((subitem: string, i: number) => ( <li key={i}> {subitem} </li> ))} </ul> ); } function Solution({ menuConfig }: { menuConfig: MenuConfig }): ReactElement { return ( <> {menuConfig.map((item: MenuItem, i: number) => ( <div key={i}> <span>{item.title}</span> <Item item={item} /> </div> ))} </> ); } export default Solution;Esto es lo que estoy pasando en:
menuConfig={[ { title: "About", }, { title: "Prices", subItems: ["Hosting", "Services"], }, { title: "Contact", subItems: ["Email", "Mobile"], }, ]} Ahora, funciona como se esperaba, si un elemento contiene subItems , se mostrará un botón Expand que, si se hace clic, solo expandirá la lista relevante.
¿Cómo debo asegurarme de que solo una lista esté abierta a la vez , dada mi implementación?
Entonces, si el usuario hace clic en Expand en un botón, las otras listas expandidas previamente deberían cerrarse.
No puedo alterar los datos que ingresan, por lo que no puedo agregar identificadores al objeto, pero los títulos son únicos.
He buscado y aunque hay algunos ejemplos, no me ayudan, simplemente no puedo entender esto.
Este es un caso de uso perfecto para el contexto React. Básicamente, desea un estado compartido entre su menú. Puede lograr esto con la perforación de apoyo, sin embargo, los contextos son una solución mucho más limpia.
const MenuContext = createContext<{ expandedState?: [number, React.Dispatch<React.SetStateAction<number>>]; }>({}); function Solution(): ReactElement { const expandedState = useState<number>(null); const value = { expandedState }; return ( <> <MenuContext.Provider value={value}> {menuConfig.map((item: MenuItem, i: number) => ( <div key={i}> <span>{item.title}</span> <Item i={i} item={item} /> </div> ))} </MenuContext.Provider> </> ); } function Item({ item, i }: { item: MenuItem; i: number }): ReactElement { const [expanded, setExpanded] = useContext(MenuContext)?.expandedState ?? []; const shown = expanded === i; return ( <> {item.subItems && ( <button onClick={() => setExpanded?.(i)}> {shown ? "Hide" : "Expand"} </button> )} {shown && <SubItem item={item} />} </> ); }