P粉4410764052023-08-31 15:23:23
You can use Redux
.
openAllAccordions
, loop through the IDs, and set the accordions belonging to that ID to open=truecloseAllAccordions
, loop through the IDs, and set the accordions belonging to that ID to open=falseP粉8091101292023-08-31 12:37:09
In a more or less arbitrary collection of component instances, it is common for some coordination to be required. One approach I've used successfully is to create a Context with an associated hook that the component can register with. This hook returns a view of the shared state and a function that modifies that state to suit your needs.
Here you can create a opener
function that stores each registered component and provide a Context for the openAll
/closeAll
functions:
const AccordionProvider = ({ children }) => { const [openers] = useState(new Set()); // 当创建时,是否应该展开新的可折叠项? //(支持递归展开是必要的) const [defaultOpen, setDefaultOpen] = useState(false); const register = useCallback( (opener) => { openers.add(opener); return () => openers.delete(opener); // 返回一个用于`useEffect`的取消注册函数 }, [openers] ); const openAll = useCallback(() => { setDefaultOpen(true); openers.forEach(opener => opener(true)), }, [setDefaultOpen, openers]); const closeAll = useCallback(() => { setDefaultOpen(false); openers.forEach(opener => opener(false)), }, [setDefaultOpen, openers]); return ( <AccordionContext.Provider value={{ register, openAll, closeAll, defaultOpen }} children={children} /> ); };
...There is also a hook called by each child component to register with the context and return the familiar toggle
/open
value:
const useAccordionAsClient = () => { const { register, defaultOpen } = useContext(AccordionContext); const [open, opener] = useState(defaultOpen); const toggle = useCallback(() => opener((open) => !open), [opener]); useEffect(() => register(opener), [register, opener]); return { toggle, open }; };
There is also a separate hook for performing batch operations which is also convenient:
const useAccordionAsManager = () => { const { openAll, closeAll } = useContext(AccordionContext); return { openAll, closeAll }; };
Please note that for simplicity, a separate opener
(aka setOpen
) function is used here as a unique identifier for each registered component. A flexible alternative is to use other identifiers, so you can open/close arbitrary accordions during navigation etc.