react的彈出層不同於以往的DOM編程,我們知道,在DOM中,彈出層事件綁定在對應的節點上即可,但是在react中,往往只能實現父子之間的傳遞控制,顯然,彈出層的層級不符合此關系。
在這里我們需要使用React官方的portals
portals可以幫助我們將子節點插入到父節點層級之外的地方
注:官方文檔使用的是class,我在這里使用的是react hook
在react
前置知識
react hook
useEffect
是了react生命周期中的componentDidMount
、componentDidUpdate
以及componentWillUnMount
三個鈎子函數的組合。
useEffect
有兩個參數useEffect
第二個參數為空數組相當於在componentDidMount
周期執行一次useEffect
第二個參數為含有某種state的數組相當於只有在這個state發生改變的時候才執行useEffect
返回一個函數相當於在componentWillUnMount
周期執行一次
實現步驟
1.首先,選擇要插入彈出層的DOM節點,在這里我參照官方文檔將整個項目分成了app-root
和model-root
兩層,我將把彈出層插入到model-root
節點中
function App(){
return(
<React.Fragment>
<div id={"app-root"}>
<Router/>
</div>
<div id={"model-root"}></div>
</React.Fragment>
)
}
export default App;
2.實現彈出層
我們按照官方文檔,先生成一個節點el作為存放我們子節點的容器,並執行ReactDOM.createPortal
ReactDOM.createPortal(child, container)
我們需要先將我們的el節點插入選定的DOM節點,然后再將portal元素插入DOM樹中,故我們先用hook在componentDidMount
階段將el插入DOM
(1)首先獲取我們要插入的DOM節點id=model-root
const modelRoot = document.getElementById('model-root');
(2)創建一個存放子節點的元素el
const [el,changEl] = useState(document.createElement('div'));
(3)在componentDidMount
階段將el節點插入model-root
//初始化工作
useEffect(()=>{
modelRoot.appendChild(el);
},[])
(4)渲染組件,執行createPortal方法
return ReactDOM.createPortal((
<Content closeModel={props.closeModel}/>
), el);
(5)在componentWillUnMount
階段移除我們的el節點
//清理工作
useEffect(()=>{
return ()=>{
modelRoot.innerHTML="";
}
})
完整代碼如下:
import React,{useState,useEffect} from 'react';
import './Model.css';
import ReactDOM from "react-dom";
import ExcelUtil from '../../utils/excelUtil';
function Content(props) {
return(
<div className={'cover'}>
<button onClick={props.closeModel}>關閉</button>
<input type='file' accept='.xlsx, .xls' onChange={(e)=>{ExcelUtil.importExcel(e)} }/>
</div>
)
}
function Model(props){
const appRoot = document.getElementById('app-root');
const modelRoot = document.getElementById('model-root');
const [el,changEl] = useState(document.createElement('div'));
//初始化工作
useEffect(()=>{
modelRoot.appendChild(el);
},[])
//清理工作
useEffect(()=>{
return ()=>{
modelRoot.innerHTML="";
}
})
return ReactDOM.createPortal((
<Content closeModel={props.closeModel}/>
), el);
}
export default Model;
這樣子子元素就出現在了我們想要的DOM層級中
3.在調用頁中引入我們的Model並定義相關觸發事件,這些與子節點向父節點的方式傳值無異
{(isShowPop == true)?<Model isShow={isShowPop} closeModel={handleInClick}/>:null}
function RegisterInUser() {
const [isShowPop,changeShowPop] = useState(false);
function handleInClick(){
changeShowPop(!isShowPop);
}
return(
<React.Fragment>
//這里是使用的地方
{(isShowPop == true)?<Model isShow={isShowPop} closeModel={handleInClick}/>:null}
<button className="ui-button ui-button-primary" onClick={handleInClick}>導入人員</button>
<button
className="ui-button ui-button-primary outExcelBtn"
type="primary"
onClick={() => {ExcelUtil.exportExcel(initColumn, attendanceInfoList,"人員名單.xlsx")}}>
導出表格
</button>
</React.Fragment>
)
}
export default RegisterInUser;