搜索

首页  >  问答  >  正文

如何将一个按钮设置为控制所有手风琴的开关?

<p>我有一个组件,使用以下代码创建一个可折叠的手风琴:</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>我在主应用程序的多个位置使用它,有时在其他手风琴中使用手风琴。在多个实例中,我实际上不知道页面上会有多少手风琴,因为它们是基于数据动态渲染的。考虑到这一点,我想在主应用程序中创建一个按钮,打开(和关闭)所有手风琴,而不需要设置一个固定的数量,并且不需要在主应用程序中渲染所有手风琴(即一些手风琴在其他组件中渲染,然后导入到主应用程序中)。</p> <p>我尝试使用ref hooks来实现这一点:</p> <ol> <li>在Collapsible组件的按钮中添加ref,并通过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>在主应用程序中添加以下ref:</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>在主应用程序中使用以下按钮:</li> </ol> <pre class="brush:php;toolbar:false;"><button onClick = {openClick}> 展开全部 </button> <button onClick = {closeClick}> 折叠全部 </button></pre> <ol start="4"> <li>在渲染时将ref添加到手风琴中:</li> </ol> <pre class="brush:php;toolbar:false;"><Collapsible label="" level="content" innerRef={childRef}></pre> <p>这实际上什么都没做,可能是因为我在第2步中尝试访问状态的方式是错误的,但我认为这是值得一试的...</p> <p>对于是否可行的任何想法吗?</p>
P粉391677921P粉391677921492 天前702

全部回复(2)我来回复

  • P粉441076405

    P粉4410764052023-08-31 15:23:23

    您可以使用Redux

    1. 当您渲染手风琴时,给它们一个特定的id并将其保存在存储中。
    2. 创建一个切片openAllAccordions,循环遍历ID,并将属于该ID的手风琴设置为open=true
    3. 创建一个切片closeAllAccordions,循环遍历ID,并将属于该ID的手风琴设置为open=false

    回复
    0
  • P粉809110129

    P粉8091101292023-08-31 12:37:09

    在一个更或多或少任意的组件实例集合中,需要一些协调是很常见的。我成功使用的一种方法是创建一个与关联钩子的Context,组件可以使用该钩子进行注册。该钩子返回共享状态的视图和修改该状态的函数,以满足您的需求。

    在这里,您可以创建一个存储每个已注册组件的opener函数并提供openAll/closeAll函数的Context:

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

    ...还有一个被每个子组件调用的钩子,用于在上下文中注册,并返回熟悉的toggle/open值:

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

    还有一个用于执行批量操作的独立钩子也很方便:

    const useAccordionAsManager = () => {
      const { openAll, closeAll } = useContext(AccordionContext);
    
      return { openAll, closeAll };
    };
    

    Sandbox

    请注意,为简单起见,这里只是使用了单独的opener(又名setOpen)函数作为每个已注册组件的唯一标识符。一个灵活的替代方案是使用其他标识符,这样您可以在导航等情况下打开/关闭任意的手风琴。

    回复
    0
  • 取消回复