對於彈出層組件,React Portals 無疑是提供了一種很好的解決方案(Protal相關也可以看這里)。 如果沒有 Portal
的話彈出層要怎么處理呢,比如React Native
環境中?
React Native
中可以使用Modal
組件,但是因為層級問題以及與其他組件的兼容性也是經常被詬病。
我們來解決這個問題。
曲徑通幽
Modal
的本質就是一個在組件樹上擁有更高顯示層級的view
,可以覆蓋整個頁面,有背景色和透明度,蒙層背景中包含彈出的顯示內容區,比如一個提示,對話框等等。
將蒙層的Modal
和內容區分開,做成父子組件的組合模式,自定義並暴露顯示子組件的方法,將子組件整個傳入Modal
中顯示,即可達到彈出內容的效果,再提供關閉Modal
的方法,用於命令式地設置Modal
的隱藏。
也就是說我們的組件滿足這些條件:
- 更高的顯示層級
- 通過方法填入子組件,並暴露顯示和隱藏
Modal
的方法
實現和效果
命令式的調用無疑需要用到Ref
了,開始實現:
-
定義組件。包含兩個
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", }, };
-
將組件放到組件樹較高層級的位置,這里直接放到了和
App
同級的位置,將定義的Ref
傳入。import Modal, { modalRef } from "./refmodal/component"; ReactDOM.render( <React.StrictMode> <App /> <Modal ref={modalRef} /> </React.StrictMode>, document.getElementById("root") );
-
測試運行效果。
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> ); }
彈出框的內容很簡單,只有一個按鈕,用於關閉當前
Modal
,show
方法將內容區整個div
傳入。效果:
到這里,一個簡易版本的Modal
已經有了初步效果。
擴展方向
將要顯示的內容做成一個組件,直接將組件view
傳入Modal
中顯示。這樣的方式,可以做成對話框,Toast,各種提示。這其中可能還需要對背景色作出修改,這些也可以通過函數參數的方式傳入到組件中來處理。
也可以將布局的關鍵屬性傳入,組件決定內容區顯示在中間還是頂部,還是底部。由此再可以擴展出actionSheet
組件。
可以將API
設計成show(view,config)
的形式,讀取config
的內容,動態設置樣式和布局顯示。
通過React
的方式添加的高層級view
顯示,這一思路完全適用於React Native
。