How can I set a button to switch all accordions on and off?
P粉391677921
P粉391677921 2023-08-30 16:57:51
0
2
451
<p>I have a component that creates a collapsible accordion using the following code: </p> <pre class="brush:php;toolbar:false;">import React, {useState} from 'react'; const Collapsible = (props) =>{ const [open, setOpen] = useState(false); const toggle = () => { setOpen(!open); } return( <div> <button className={props.level} onClick={toggle}>{props.label}</button> {open && ( <div className="toggle"> {props.children} </div> )} </div> ) } export default Collapsible;</pre> <p>I use it in several places in the main application and sometimes in other accordions. In multiple instances, I actually had no idea how many accordions there would be on the page because they were rendered dynamically based on the data. With this in mind, I want to create a button in the main application that turns on (and off) all accordions without having to set a fixed number, and without having to render all accordions in the main application (i.e. some accordions in other component and then imported into the main application). </p> <p>I tried using ref hooks to achieve this: </p> <ol> <li>Add ref in the button of the Collapsible component and access it from the parent component through props: </li> </ol> <pre class="brush:php;toolbar:false;"><button className={props.level} onClick={toggle} ref={props.innerRef}>{props.label}</button> ;</pre> <ol start="2"> <li>Add the following ref in the main application: </li> </ol> <pre class="brush:php;toolbar:false;">const childRef = useRef(); const openClick = () => { childRef.state = true; } const closeClick = () => { childRef.state = false; }</pre> <ol start="3"> <li>Use the following buttons in the main application: </li> </ol> <pre class="brush:php;toolbar:false;"><button onClick = {openClick}> Expand All </button> <button onClick = {closeClick}> Collapse all </button></pre> <ol start="4"> <li>Add ref to accordion when rendering: </li> </ol> <pre class="brush:php;toolbar:false;"><Collapsible label="" level="content" innerRef={childRef}></pre> <p>This actually does nothing, probably because the way I was trying to access the state in step 2 was wrong, but I figured it was worth a try...</p> <p>Any ideas on if this is possible? </p>
P粉391677921
P粉391677921

reply all(2)
P粉809110129

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 (
    
  );
};

...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 };
};

Sandbox

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.

Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!