React中的模式對話方塊
在16.x版本之後React提供了Protals功能來解決模式對話方塊不在Dom根節點導致的一些BUG。除了Protal還有更多的方法去解決這些問題,本文來自David Gilbertson的部落格,詳細解釋了React中模式對話方塊的一些問題,以及他給出的解決方案,在瞭解Protals之前閱讀這篇內容,能讓你更加明白Protal的重要性。
對於React的模式對話方塊,有很多方法可以實現但是並沒有一個絕對正確的方法。這句話怎麼理解呢?讓我們先看看一個模式對話方塊的特性:
- 能夠浮現在最上層,阻止使用者的其他操作。
- 能夠處理滑鼠和鍵盤事件,例如關閉視窗事件。
- 接受外部傳入一個回撥函式,當使用者進行某些操作的時候呼叫他,例如點選“確定”或“取消”按鈕。
- 接受外部引數,可以設定大小、文字、處理器等等。
模式對話方塊的實現思路
下面的這些圖片是常見模式對話方塊的例子:
這些模式對話方塊都有一個全域性的背景遮罩層、有頭部或描述內容、有一些功能按鈕、可以隨意設定的寬度和高度、位置居中。
在React中有三種方式實現模式對話方塊:
- 使用一個常規的元件作為一個模式對話方塊的包裝元件,然後將我們自定義的內容作為子元件傳遞給模式對話方塊。例如這個專案:https://github.com/reactjs/react-modal。
- 將模式對話方塊放置到HTML結構的頂層,將其設定為 document.body 的子元素。例如:https://github.com/tajo/react-portal
- 將模式對話方塊作為整個元件結構中的頂層元件(根元素的子元件),通過全域性的資料來控制他顯示或隱藏。
那這三種實現方式有什麼問題呢:
第一種方式有定位問題。如果你用這種方式實現模式對話方塊,你的HTML上下文會影響當前模式對話方塊的展示效果,所以這種方式很有可能會出現一些意向不到的問題。你真的認為 position: fixed 可以讓某個元素相對與瀏覽器視窗絕對定位嗎?請看這個例子: https://output.jsbin.com/fepime/,使用開發人員工具看看 .top-div和 .fixed-div 的樣式你就懂了。
第二種方式首先對於單元測試不友好,因為我們不得不把對話方塊作為body的子元素(或者其他某個真實DOM的子元素)來顯示,那麼得有瀏覽器的真實DOM才能看到效果。而且這種方式看起來挺“駭客”的,我們按照單向資料流的思路開發了整套個標準合理的React元件,最後不得不用 ReactDOM.unstable_renderSubtreeIntoContainer() 方法裝載一個元件到body元素中,最終可能會導致虛擬DOM與真實DOM不一致或者服務端渲染遇到問題。‘unstable’字首的含義是React官方明確告訴你:這玩意有坑,踩上了別怪我。詳情請看React官方對unstable_renderSubtreeIntoContainer的說明。
第三種方式在筆者看來是最合理最優秀的,下面就談談這種實現方式的思路。
全域性資料流控制模式對話方塊
實際上就是用flux或redux的方式去控制對話方塊顯示或關閉。如果之前用過flux之類思路的工具,後面的內容分分鐘就理解了。
先看下模式對話方塊的元件結構:
- App.jsx——整個工程的根元件,通常不會在這裡有什麼特殊的處理。它首先會渲染其他所有的頂層元件,然後再最後渲染模式對話方塊元件。
- ModalConductor.jsx——模式框的管理元件,由他來控制當前應該渲染哪個模式框。
- SignIn.jsx、EditScreen.jsx等元件——具體樣式的模式對話方塊。
在這些元件之外,還有store來儲存全域性模式對話方塊的相關資料。store.currentModal 用於指示顯示哪個模式框的字串,如果為 null 則表示沒有任何模式框要顯示,所以整個工程一次只顯示一個模式框。
下面我們看看元件實現過程。
首先我們在任何位置都可以修改 store 。當我們通過某種方式將 store.currentModal 的值修改為 signIn 後,ModalConductor 會觸發重新渲染並在內部判斷要渲染 SignIn 元件。
這是 ModalConductor 的示意程式碼,通過switch語句判斷要顯示的元件:
import React from `react`;
import ExportDataModal from `./ExportDataModal.jsx`;
import SignInModal from `./SignInModal.jsx`;
import FeedbackModal from `./FeedbackModal.jsx`;
import BoxDetailsModal from `./BoxDetailsModal.jsx`;
const ModalConductor = props => {
switch (props.currentModal) {
case `EXPORT_DATA`:
return <ExportDataModal {...props}/>;
case `SOCIAL_SIGN_IN`:
return <SignInModal {...props}/>;
case `FEEDBACK`:
return <FeedbackModal {...props}/>;
case `EDIT_BOX`:
return <BoxDetailsModal {...props}/>;
default:
return null;
}
};
export default ModalConductor;
下面模式對話方塊元件的程式碼結構:
import React from `react`;
import ModalWrapper from `../ModalWrapper.jsx`;
const SignIn = props => {
const signIn = provider => {
props.hideModal();
props.signIn(provider);
};
return (
<ModalWrapper
{...props}
title="Sign in"
width={400}
showOk={false}
>
<p>Choose your flavor</p>
<button onClick={() => signIn(`facebook`)}>Facebook</button>
<button onClick={() => signIn(`google`)}>Google</button>
<button onClick={() => signIn(`twitter`)}>Twitter</button>
</ModalWrapper>
);
};
export default SignIn;
他內部使用了一個名為 ModalWrapper 的包裝元件,用來顯示模式對話方塊的效果,可以直接使用https://github.com/reactjs/react-modal或者自己實現,如下是一個模式框的包裝元件:
import React from `react`;
const {PropTypes} = React;
const ModalWrapper = props => {
const handleBackgroundClick = e => {
if (e.target === e.currentTarget) props.hideModal();
};
const onOk = () => {
props.onOk();
props.hideModal();
};
const okButton = props.showOk
? (
<button
onClick={onOk}
disabled={props.okDisabled}
>
{props.okText}
</button>
) : null;
return (
<div onClick={handleBackgroundClick}>
<header>
<h1>{props.title}</h1>
<button onClick={props.hideModal}>Close</button>
</header>
{props.children}
{okButton}
</div>
);
};
ModalWrapper.defaultProps = {
title: ``,
showOk: true,
okText: `OK`,
okDisabled: false,
width: 400,
onOk: () => {}
};
export default ModalWrapper;
相關文章
- c#中的模態對話方塊和非模態對話方塊C#
- javascript中的彈出對話方塊JavaScript
- 對話方塊中對成批控制元件的操作 (轉)控制元件
- 對話方塊函式函式
- 登入對話方塊
- Java 中彈出對話方塊的幾種方式Java
- [MFC]選擇目錄對話方塊和選擇檔案對話方塊
- 建造者模式打造隨心所欲的Android對話方塊模式Android
- flutter demo (四):對話方塊Flutter
- Javascript檔案對話方塊JavaScript
- VUE:點選開啟的對話方塊外面時,對話方塊總是被關閉Vue
- react.js自定義pc桌面端對話方塊|react仿layer彈窗ReactJS
- IE中非模式對話方塊(showModelessDialog)應用 (轉)模式
- 一個Flex 對話方塊的坑Flex
- 在 Flutter 使用 GetX 對話方塊Flutter
- Qt 對話方塊新增工具欄QT
- 如何自學qt(4)——對話方塊QT
- QT 等待對話方塊/進度QT
- android 多項對話方塊Android
- Android Dialog對話方塊Android
- DialogPane對話方塊佈局
- 視窗和對話方塊居中對齊
- JavaFX 如何使用內建的對話方塊Java
- 如何在 Bash Shell 指令碼中顯示對話方塊指令碼
- Flutter Widgets 對話方塊-DialogFlutter
- 常用元件 / 對話方塊及選單元件
- Qt標準對話方塊實現QT
- TWebBrowser禁止彈出Alert對話方塊Web
- android常用對話方塊封裝Android封裝
- JavaScript彈出儲存對話方塊JavaScript
- java Swing詢問對話方塊Java
- CSS帶有箭頭的對話方塊效果CSS
- 對話方塊背景色的設定 (轉)
- Android 中的特殊攻擊面(一)——邪惡的對話方塊Android
- 基於React.js網頁版彈窗|react pc端自定義對話方塊元件RLayerReactJS網頁元件
- word中打不開“特殊符號”對話方塊的解決方法符號
- flutter佈局-7-About對話方塊Flutter
- Android對話方塊Dialog深度剖析Android