搜尋

首頁  >  問答  >  主體

如何將一個按鈕設定為控制所有手風琴的開關?

<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粉391677921453 天前656

全部回覆(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
  • 取消回覆