题目大概是要实现一个dialog的模板,你可以应用他实现很多功能,比如说某个操作的确认提醒等,我们要做的实现这样一个可应用都多个功能的dialog,只要你想要显示的组件写好,外部嵌套这样一个dialog组件,就能实现
首先我们先实现一个简单的Dialog
不妨百度一下antd的对话框Modal
功能如下:
1.点击出现,周围灰色,中间一个对话框,点击周围关闭对话框,点击对话框不会关闭
2.这里注意,当对话框出现,原本页面其他事件点击都不会被触发
思路:
很简单,设置一个状态,isShow来表示dialog是否出现,页面添加一个按钮(其他的也可以,目的是触发dialog出现),初始化dialog为false,点击变为true,此时出现的dialog会覆盖整个页面,给这个覆盖了整个页面的dialog_box挂载点击事件,监听如果点击的周围灰色区域,则再把isShow设为false,关闭dialog。
这个状态可能会被很多地方使用,所以直接redux存储,
步骤:
1.下载安装react-redux和redux
yarn add redux react-redux
2.创建store
3.创建reducer
4.包裹并传入参数store={store}
代码如下:
首先整体目录:

store.js
这里使用了combineReducers,主要是熟悉一下,好久不用总要看一眼
import {combineReducers, createStore} from 'redux'
import dialogReducer from "./reducer/dialogReducer";
const reducer=combineReducers({
dialogReducer
})
const store=createStore(reducer)
export default store
dialogReducer.js
var dialogReducer=(prevState={
isShow:false,
},action)=>{
var state={...prevState};
switch(action.type){
case 'DIALOG_SHOW':{
return {
...state,
isShow:action.value,
}
}
default:{
return state
}
}
}
export default dialogReducer
index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux';
import store from './redux/store'
import App from './App';
import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
这里有一步监听是否是dialog对话框还是周围灰色区域,大致如下操作:
1.创建一个ref(useRef创建),挂载到整个Dialog上,这样就能获取到整个dialog_box组件,


2.然后添加监听事件,以及出发的处理函数,这个处理函数是要判断当前点击的组件的class类名是否是dialog_box(因为这个点击会精确到具体组件,即点击子组件会返回子组件)
首先,节点及其类名如下:

添加监听

这里函数dialogFun的参数e是当前点击到的组件,如果是灰色区域,则返回整体dialog_box,点击中间对话框,则返回dialog,然后可以通过e.target.className获取组件类名用于比较,这里props.close()是将isShow设为false的操作,具体定义放在了父组件App上
所以综上:
ref用来添加监听事件
e.target用来监听点击到的组件
注意ref.current可以获取被挂载的组件,e.target.className可以获去class类名
代码如下
App.js
这是一个全局的父组件,所以我直接将处理显示的函数放在了这里然后传递给子组件,这样子组件就不需要再connect获取redux中的状态
(下面组件中的嵌套可以先忽略)
import React,{useEffect} from 'react'
import { connect } from 'react-redux'
import Dialog from './components/Dialog'
import './App.css'
import DialogCancle from './components/DialogCancle'
function App(props) {
const close=function(){
props.changeShow(false)
}
useEffect(() => {
console.log(props.isShow)
}, [props.isShow])
return (
<div style={{height:'100vh',width:'100vw'}}>App
<button onClick={()=>{props.changeShow(true)}}>吃点什么</button>
{props.isShow?<Dialog close={close}><DialogCancle close={close}/></Dialog>:''}
</div>
)
}
const mapStateToProps=function(state){
return {
isShow:state.dialogReducer.isShow,
}
}
const mapStateToDispatch=function(dispatch){
return {
changeShow(isShow){
dispatch({type:"DIALOG_SHOW",value:isShow})
}
}
}
export default connect(mapStateToProps,mapStateToDispatch)(App)
Dialog.js
import React,{useRef,useEffect} from 'react';
import './Dialog.css'
export default function Dialog(props) {
useEffect(() => {
ref.current.addEventListener('click',dialogFun)
}, [props.isShow])
const dialogFun=function(e){
console.log(ref.current)
if(e.target.className==='dialog_box'){
props.close()
}
}
var ref=useRef()
return (
<div value="dialog_box" ref={ref} className="dialog_box">
<div value="dialog" className="dialog">
<div style={{textAlign:'center',color:'#1890FF',fontSize:'18px',paddingTop:'10px',paddingBottom:'10px'}}>Dialog</div>
<div >
{props.children}
</div>
</div>
</div>
);
}
Dialog.css
css代码可以自定义,我就随便写了些,主要不要忘了zIndex的值要大于原本页面,不然无法覆盖
.dialog{
/* border-radius: 10px; */
background-color: white;
height:150px;
width:400px;
margin:0 auto;
margin-top:100px;
transition: 3s linner;
}
.dialog_box{
width:100%;
height:100%;
position:fixed;
z-index:10001;
top:0;
left:0;
background-color: rgba(3,3,3,0.4);
/* opacity: 0.4; */
}

到此简单的Dialog他已经完成,接下来我们要实现一个中间传入组件就能显示在Dialog里面的功能
看了网上,有很多实现方法,比如render什么的,这里使用插槽,但一直没找到怎么往插槽里直接传参,失败
直接在组件内部添加一个{props.children}用来占位置,这时你只需将你想要显示的子组件外部嵌套一层Dialog组件即可
<Dialog close={close}>
<DialogCancle close={close}/>
</Dialog>
Dialog组件的内部render
return (
<div value="dialog_box" ref={ref} className="dialog_box">
<div value="dialog" className="dialog">
<div style={{textAlign:'center',color:'#1890FF',fontSize:'18px',paddingTop:'10px',paddingBottom:'10px'}}>Dialog</div>
<div >
{props.children}
</div>
</div>
</div>
);
简单到不能再简单的子组件DialogCancle
import React,{useEffect} from 'react'
import Dialog from './Dialog'
export default function DialogCancle(props) {
useEffect(() => {
console.log(props)
}, [])
return (
<div >
<p>今天中午吃什么??</p>
<button>不吃饭</button>
<button onClick={()=>props.close()} >取消</button>
</div>
)
}

所以无论你的DialogCancle内部怎么写,只要你外部嵌套Dialog,就可以将组件内容显示再对话框内
十分粗糙的实现这个样一个小demo,有问题请指出