制作一個命令式的 React 彈出層組件 (適用 React Native)


對於彈出層組件,React Portals 無疑是提供了一種很好的解決方案(Protal相關也可以看這里)。 如果沒有 Portal的話彈出層要怎么處理呢,比如React Native環境中?

React Native中可以使用Modal組件,但是因為層級問題以及與其他組件的兼容性也是經常被詬病。

我們來解決這個問題。

曲徑通幽


Modal的本質就是一個在組件樹上擁有更高顯示層級的view,可以覆蓋整個頁面,有背景色和透明度,蒙層背景中包含彈出的顯示內容區,比如一個提示,對話框等等。

將蒙層的Modal和內容區分開,做成父子組件的組合模式,自定義並暴露顯示子組件的方法,將子組件整個傳入Modal中顯示,即可達到彈出內容的效果,再提供關閉Modal的方法,用於命令式地設置Modal的隱藏。

也就是說我們的組件滿足這些條件:

  • 更高的顯示層級
  • 通過方法填入子組件,並暴露顯示和隱藏Modal的方法

實現和效果


命令式的調用無疑需要用到Ref了,開始實現:

  1. 定義組件。包含兩個state,一個view,一個控制顯示與隱藏的flag。暴露出兩個方法,顯示和關閉。需要顯示的內容view通過方法參數傳入

    import React, { useImperativeHandle, useState } from "react";
    
    function Modal(props, ref) {
      const [view, setView] = useState([]);
      const [isShow, setIsShow] = useState(0);
    
      useImperativeHandle(ref, () => ({
        init: (view) => {
          setView(view);
          setIsShow(true);
        },
        close: () => setIsShow(false),
      }));
    
      if (isShow) {
        return <div style={sts.wrap}>{view}</div>;
      } else {
        return null;
      }
    }
    
    export default React.forwardRef(Modal);
    export const modalRef = React.createRef();
    export const TopModal = { show, close };
    
    function show(view) {
      modalRef.current.init(view);
    }
    function close() {
      modalRef.current.close();
    }
    
    // 蒙層 style
    const sts = {
      wrap: {
        zIndex: 100,
        top: 0,
        left: 0,
        width: "100%",
        height: "100%",
        position: "absolute",
        backgroundColor: "rgba(0,0,0,0.6)",
        display: "flex",
        justifyContent: "center",
        alignItems: "center",
      },
    };
    
    
  2. 將組件放到組件樹較高層級的位置,這里直接放到了和App同級的位置,將定義的Ref傳入。

    import Modal, { modalRef } from "./refmodal/component";
    
    ReactDOM.render(
      <React.StrictMode>
        <App />
        <Modal ref={modalRef} />
      </React.StrictMode>,
      document.getElementById("root")
    );
    
  3. 測試運行效果。

    export default function ModalRefTest() {
      function openModal() {
        const view = (
          <div style={{ width: 100, height: 100, backgroundColor: "#FFF" }}>
            <button onClick={close}>關閉</button>
          </div>
        );
        TopModal.show(view);
      }
      function close() {
        TopModal.close();
      }
      return (
        <div>
          <button onClick={openModal}>打開</button>
        </div>
      );
    }
    

    彈出框的內容很簡單,只有一個按鈕,用於關閉當前Modalshow方法將內容區整個div傳入。

    效果:

到這里,一個簡易版本的Modal已經有了初步效果。

擴展方向


將要顯示的內容做成一個組件,直接將組件view傳入Modal中顯示。這樣的方式,可以做成對話框,Toast,各種提示。這其中可能還需要對背景色作出修改,這些也可以通過函數參數的方式傳入到組件中來處理。

也可以將布局的關鍵屬性傳入,組件決定內容區顯示在中間還是頂部,還是底部。由此再可以擴展出actionSheet組件。

可以將API設計成show(view,config)的形式,讀取config的內容,動態設置樣式和布局顯示。

通過React的方式添加的高層級view顯示,這一思路完全適用於React Native


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM