最近在用react开发项目,遇到一个需求——开发一个弹框组件。在react中创建一个组件是很简单的,只需要使用class创建并引入就可以了,但是要做到可以用js调用这个组件而不是写在jsx结构里,那就需要用到ReactDOM.render这个方法了。
首先先来屡一下需求:
1、弹框里的可配置字段:标题文字,提示文字,确认和取消按钮的显示隐藏以及文字。
2、点击确认和取消按钮后,可以触发相应的事件。
3、是否为短提示,短提示的时候,确认和取消按钮隐藏,并且2s后消失。
接下来用两种方法创建一个弹框组件,并比较一下这两种的差异。
下面先来实现一个普通的写在jsx结构里的组件:
弹框组件:DialogAlert.js
import React, { Component } from 'react'; import './index.scss'; class DialogAlert extends Component { constructor(props){ super(props); this.state = { alertStatus:false, alertTitle:'提示', //标题 alertTip:'网络错误', //提示 cancelText:'取消', confirmText:'确认', isShortTip:false, //是否为短提示,短提示的情况下不显示'取消''确认'(且2s后消失),且优先级最高,其他配置无效 isShowCancel:true, //是否显示确认按钮 isShowConfirm:true, //是否显示确认按钮 cancelCallbackFn:function(){}, //取消 回调函数 confirmCallbackFn:function (){}//确认 回调函数 } } componentWillReceiveProps(nextProps) { let options = nextProps.dialogOpt || {}; //如果是短提示 if(options.isShortTip){ options.isShowCancel = false; options.isShowConfirm = false; setTimeout(()=>{ this.close() },2000) } this.setState({ ...options }) } //取消 cancel = () => { this.state.cancelCallbackFn(); this.close() } //确认 confirm = () => { this.state.confirmCallbackFn(); this.close() } close = () => { this.setState({ alertStatus:false }) } render(){ let opts = this.state; return ( <div className="dialog-wrap" style={opts.alertStatus ? {display:'block'}:{display:'none'}}> <div className="dialog-box"> <h6>{opts.alertTitle}</h6> <p>{opts.alertTip}</p> {!opts.isShowCancel && !opts.isShowConfirm ? null : ( <div> {opts.isShowCancel ? (<span onClick={ () => this.cancel() }>{opts.cancelText}</span>) : null} {opts.isShowConfirm ? (<span className="confirm" onClick={ () => this.confirm() }>{opts.confirmText}</span>) : null} </div> )} </div> </div> ) } } export default DialogAlert;
这里的数据更新用到了componentWillReceiveProps这个生命周期,当props发生变化时执行,初始化render时不执行,在这个回调函数里面,你可以根据属性的变化,通过调用this.setState()来更新你的组件状态,旧的属性还是可以通过this.props来获取,这里调用更新状态是安全的,并不会触发额外的render调用。
调用页面index.js
在state中定义可配置字段的变量
import DialogAlert from '../../widget/DialogAlert/index'; //省略了组件的js this.state = { dialogOpt:{ alertStatus:false, alertTip:'我是自定义的内容', cancelText:'取消2', confirmText:'确认2', isShortTip:false, isShowCancel:true, //是否显示确认按钮 isShowConfirm:true, //是否显示确认按钮 cancelCallbackFn:function(){ alert(0); }, //取消 回调函数 confirmCallbackFn:function (){ alert(1); }//确认 回调函数 }, //其他数据 };
在jsx中埋好对应的组件结构
<div onClick={()=>(this.alertdialog())}>点击触发弹框</div> <DialogAlert dialogOpt={this.state.dialogOpt}></DialogAlert>
添加触发事件
alertdialog(){ let opts = { alertStatus:true } let _dialogOpt = Object.assign(this.state.dialogOpt,opts) this.setState({ dialogOpt:_dialogOpt }) }
这样就完成一个普通的弹框。总感觉这样写的一个组件弹框有点冗余,复用起来也比较麻烦——在state里配置所有自定义的变量,并改动jsx结构,还需要注意写入jsx结构时弹框的层级问题。
接下来我们来实现一种可动态调用的组件:
原理是创建一个div,并插入到body里面,用这个div当容器,使用render渲染组件,通过改变组件的state来控制组件的显示和隐藏。
弹框组件:DialogAlert.js
import React, { Component } from 'react'; import ReactDOM from 'react-dom'; import './index.scss'; //调用方法 // DialogAlert.open({ // alertTitle:'提示2', // alertTip:"页面加载失败,是否重新加载?", // cancelText:'取消', // confirmText:'重新加载', // isShortTip:true, // isShowCancel:true, // isShowConfirm:true, // cancelCallbackFn:function(){ // console.log('取消了') // }, // confirmCallbackFn:function (){ // console.log("确认了..."); // } // }); class DialogBox extends Component { constructor(props){ super(props); this.state = { alertStatus: false, //是否显示提示框 alertTitle:'提示', //标题 alertTip:'网络错误', //提示 cancelText:'取消', confirmText:'确认', isShortTip:false, //是否为短提示,短提示的情况下不显示'取消''确认'(且2s后消失),且优先级最高,其他配置无效 isShowCancel:true, //是否显示确认按钮 isShowConfirm:true, //是否显示确认按钮 cancelCallbackFn:function(){}, //取消 回调函数 confirmCallbackFn:function (){}//确认 回调函数 } } //打开提示框 open = (options) => { options = options || {}; //如果是短提示 if(options.isShortTip){ options.isShowCancel = false; options.isShowConfirm = false; setTimeout(()=>{ this.close() },2000) } options.alertStatus = true; this.setState({ ...options }) } //取消 cancel = () => { this.state.cancelCallbackFn(); this.close() } //确认 confirm = () => { this.state.confirmCallbackFn(); this.close() } close = () => { this.setState({ alertStatus:false }) } render(){ let opts = this.state; return ( <div className="dialog-wrap" style={opts.alertStatus? {display:'block'}:{display:'none'}}> <div className="dialog-box"> <h6>{opts.alertTitle}</h6> <p>{opts.alertTip}</p> {!opts.isShowCancel && !opts.isShowConfirm ? null : ( <div> {opts.isShowCancel ? (<span onClick={ () => this.cancel() }>{opts.cancelText}</span>) : null} {opts.isShowConfirm ? (<span className="confirm" onClick={ () => this.confirm() }>{opts.confirmText}</span>) : null} </div> )} </div> </div> ) } } let div = document.createElement('div'); document.body.appendChild(div); let DialogAlert = ReactDOM.render(<DialogBox /> ,div); //返回实例 export default DialogAlert;
调用页面index.js
import DialogAlert from '../../widget/DialogAlert/index'; //省略了组件的js DialogAlert.open({ alertTip:"加载失败,是否重新加载?", confirmText:'重新加载', cancelCallbackFn:()=>{ window.history.back(); }, confirmCallbackFn:()=>{ //todo... } })
这里用到了ReactDOM.render,官方文档说这个方法目前会返回了对根组件实例的引用,所以我们可以调用到里面的open方法。但是官方文档中目前应该避免使用返回的引用,因为它是历史遗留下来的内容。为了以后的react更新迭代的兼容,我们可以省去动态插入组件的过程,改为写在jsx中,并设置ref,使用this.refs.xxx获取当前组件的实例,以便调用实例方法。
只需引入之后,直接调用就可以了。这样写的好处是解决了弹框的层级问题,也不用去改动jsx结构,其他页面复用起来更加方便快捷。
这两种方法在组件的定义上并没有很大的不同,只是在更新状态的时候有差异。第一种方法是在componentWillReceiveProps这个生命周期中监听父组件的值的变化再更新到state上,第二中方法是直接调用实例的open方法通过获取参数将值更新到state上。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持好代码网。