以前的需求有一些弹窗,弹窗内容比较复杂,在 React 项目中怎么方便高效地实现一个业务弹窗组件呢?经历了一段时间的演变,终于找到了比较方便的方法……bash
下面以简单的设置文案的弹窗为例列举一下弹窗的实现。antd
使用的基础弹窗组件是 antd modal。app
比较常见的写法就是经过标签实现,也是我最开始经常使用的,但总以为不爽。ui
🎉 完整 demothis
弹窗组件的实现:spa
class DialogCustom extends React.Component {
constructor(props) {
super(props);
this.state = { text: '' };
}
handleOk = () => {
this.props.onOk(this.state.text);
this.props.onClose();
}
onChange = (e) => {
this.setState({ text: e.target.value });
}
render() {
const { visible, onClose } = this.props;
return (
<Modal title="设置文案" visible={visible} onOk={this.handleOk} onCancel={onClose}>
<Input value={this.state.text} onChange={this.onChange} />
</Modal>
);
}
}
复制代码
使用组件:code
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
visible: false, // 要增长一个 visible 很麻烦有木有
text: ''
};
}
handleOk = (v) => {
this.setState({ text: v });
}
showDialog = () => {
this.setState({ visible: true });
}
handleClose = () => {
this.setState({ visible: false });
}
render() { // 只能经过标签调用
return (<div>
<Button onClick={this.showDialog}>设置文案</Button>
<DialogCustom visible={this.state.visible} onOk={this.handleOk} onClose={this.handleClose} />
<div>{this.state.text}</div>
</div>);
}
}
复制代码
本方法的麻烦之处在于:component
实现一个弹窗组件时每次都要引入 Modal 组件;cdn
使用组件时,只能用标签的方式,且须要有一个 visible state 控制弹窗状态;blog
这样写一个其实也没什么,多了之后就烦了……
针对上面使用组件的麻烦,但愿使用组件时能够经过 xxx.show()
的方式快捷调用,不须要标签,不须要经过 visible state 控制。
解决:给组件加上 show 方法。跟上一方法的差异就是增长了一个 show 方法,另外 <Modal />
的 visible
属性直接设为 true
便可
🎉 完整 demo
弹窗组件的实现:
class DialogCustom extends React.Component {
static show = params => {
let container = document.createElement("div");
document.body.appendChild(container);
function closeHandle() {
ReactDOM.unmountComponentAtNode(container);
document.body.removeChild(container);
container = null;
}
ReactDOM.render(<DialogCustom {...params} onClose={closeHandle} />, container);
};
...
render() {
return (<Modal ... visible={true}>...</Modal>);
}
}
复制代码
使用组件:
DialogCustom.show({ onOk: this.handleOk }); // 调用 show 想弹就弹,弹得响亮;直接可关闭,不用再增长 visible state 去控制
复制代码
本方法的麻烦之处在于:
这样写几个,多了之后仍是烦了……
针对 “实现一个弹窗组件时每次都要引入 Modal,都要重写一次 show 方法” 的问题,能够用高阶组件解决
🎉 完整 demo
高阶组件:
const withDialog = WrappedComponent => {
function EnhancedComponent(props) {
const { title, onClose, ...others } = props;
return (<Modal visible={true} title={title || WrappedComponent.title} footer={<div />}>
<WrappedComponent {...others} onClose={onClose} />
</Modal>);
}
EnhancedComponent.show = params => {
let container = document.createElement("div");
document.body.appendChild(container);
function closeHandle() {
ReactDOM.unmountComponentAtNode(container);
document.body.removeChild(container);
container = null;
}
ReactDOM.render(<EnhancedComponent {...params} onClose={closeHandle} />, container);
};
return EnhancedComponent;
};
复制代码
设置文案的组件几乎只要处理自己的逻辑,跟弹窗相关的有两个点:
若是不须要弹窗了,那么不使用高阶组件,直接使用 SetText 便可(此处应该要再处理下 footer)。
业务组件:
@withDialog // 使用高阶组件,很关键
class SetText extends React.Component { // 只要处理自己的逻辑,几乎不用在乎弹窗
static title = "设置文案";
static defaultProps = {
onClose: () => {}
};
constructor(props) {
super(props);
this.state = { text: "" };
}
onChange = e => {
this.setState({ text: e.target.value });
};
handleOk = () => {
this.props.onOk(this.state.text);
this.props.onClose();
};
render() {
return (<div>
<Input value={this.state.text} onChange={this.onChange} />
<div>
<Button onClick={this.handleOk}>肯定</Button>
<Button onClick={this.props.onClose}>取消</Button>
</div>
</div>);
}
}
复制代码
使用组件:
SetText.show({ onOk: this.handleOk });
复制代码
本方法的问题在于:
经过这种方式,几乎不用侵入你的组件,你的组件就能够披上一个弹窗。
注:文本案例基于 antd modal 实现,但不限于 antd modal,你能够本身封装一个基础弹窗就能够实现弹弹弹。