快速实现 React 业务弹窗组件
之前的需求有一些弹窗,弹窗内容比较复杂,在 React 项目中怎么方便高效地实现一个业务弹窗组件呢?经历了一段时间的演变,终于找到了比较方便的方法……
下面以简单的设置文案的弹窗为例列举一下弹窗的实现。
使用的基础弹窗组件是 antd modal。
一般写法
比较常见的写法就是通过标签实现,也是我最开始常用的,但总觉得不爽。
🎉 完整 demo
弹窗组件的实现:
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>
);
}
}
使用组件:
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>);
}
}
本方法的麻烦之处在于:
-
实现一个弹窗组件时每次都要引入 Modal 组件;
-
使用组件时,只能用标签的方式,且需要有一个 visible state 控制弹窗状态;
这样写一个其实也没什么,多了以后就烦了……
快捷调用
针对上面使用组件的麻烦,希望使用组件时可以通过 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 方法
这样写几个,多了以后还是烦了……
高阶组件
针对 “实现一个弹窗组件时每次都要引入 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;
};
设置文案的组件几乎只要处理本身的逻辑,跟弹窗相关的有两个点:
- 高阶组件赐予了业务组件一个 onClose 的属性以拥有关闭弹窗的权利,业务组件要注意下关闭弹窗的时机。
- footer 就由业务组件全权承包了……
如果不需要弹窗了,那么不使用高阶组件,直接使用 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 });
本方法的问题在于:
- footer 一定需要自定义,注意保持样式统一
通过这种方式,几乎不用侵入你的组件,你的组件就可以披上一个弹窗。
注:文本案例基于 antd modal 实现,但不限于 antd modal,你可以自己封装一个基础弹窗就可以实现弹弹弹。
hide