利用async/await 关键字,我们可以优雅的处理异步逻辑。
比如调用网络接口可以直接await fetch(……)
事实上,在处理页面交互逻辑时使用async/await,也可以起到很好的解耦作用,写出更清晰的代码。
比如获取Modal弹框的交互结果:
后台系统在交互设计上经常使用模态弹框。很多UI框架都提供了方便易用的Modal组件,比如Ant design:
先来看一下antd官方的demo是怎么做的:
import { Modal, Button } from 'antd';class App extends React.Component { state = { visible: false } // 还是需要维护一个visible state用来控制Modal的显示状态 showModal = () => { this.setState({ visible: true, }); } handleOk = (e) => { this.setState({ visible: false, }); } handleCancel = (e) => { this.setState({ visible: false, }); } render() { return (); }}ReactDOM.render(Some contents...
Some contents...
Some contents...
, mountNode);复制代码
以上代码可以看出:受制于React通常的写法,在频繁使用Modal的场景下Antd的封装使用起来还是有些繁琐
- 还是需要维护一个visible state,用来控制Modal是否显示
- 如果外层组件只需要获取Modal内操作的结果,需要用props传递一个onChange方法进来,或者借助笨重的全局状态管理机制
- 大量使用Modal时,会产生海量的重复代码
- ……
当然,作为中后台系统的优秀解决方案,Antd的小伙伴帮我们封装了Modal.methods可以快速的创建一些简易的弹窗:
Modal.confirm({ title: 'Confirm', content: 'Bla bla ...', okText: '确认', cancelText: '取消',});复制代码
这是一个不错的思路,但是Antd官方提供的methods功能非常有限。
我们来看一看能否在Antd Modal的基础上做一些改进:
希望能够实现result = await Modal.open()
这样的调用方式。
一种思路是在实现一个Modal class,上面有static open这样一个方法,当调用open时,向页面插入一个modal,类似上面Modal.confirm的做法。
另一种办法,可以先在页面实例化一个modal,需要open的时候利用ref attribute获取到已经实例化的modal,并调用它的open 方法。 如下:
import React from 'react';import { Modal, Input } from 'antd';export default class AsyncModalDemo extends React.Component { state = { visible: false, text: '' } open = () => { this.setState({ visible: true, }); return new Promise((resolve, reject) => { this.resolve = resolve; this.reject = reject; }); } close = () => { this.setState({ visible: false, }); if (this.reject && typeof this.reject === 'function') { this.reject('cancel'); } } handleOk = () => { if (typeof this.resolve === 'function') { this.resolve(this.state.text); } delete this.reject; this.close(); } render() { return}}复制代码 helloyou can check result in devtool{ const v = (e && e.target && e.target.value) || ''; this.setState({ text: v, }) }} value={this.state.text} >
调用方式如下:
import React from 'react';import './App.css';import { Button } from 'antd';import AsyncModal from './components/async_modal';class App extends React.Component { handleOpenModal = async () => { if (this.refs.asyncModal) { const result = await this.refs.asyncModal.open(); console.log(result) } } render() { return (); }}export default App;复制代码
这样,我们就可以使用await 获取到Modal内到交互结果了,并且做到了较好的解耦。
但是每次都要在Modal类里面写一堆open、close、handleOk这样的method,感觉很烦。那么有没有比较好的方式,实现代码复用呢?
首先尝试一下继承:
import React from 'react';import { Modal, Input } from 'antd';class AsyncModalBase extends React.Component { state = { visible: false, } open = () => { this.setState({ visible: true, }); return new Promise((resolve, reject) => { this.resolve = resolve; this.reject = reject; }); } close = () => { this.setState({ visible: false, }); if (this.reject && typeof this.reject === 'function') { this.reject('cancel'); } }}export default class AsyncModalDemo extends AsyncModalBase { state = { text: '' } handleOk = () => { if (typeof this.resolve === 'function') { this.resolve(this.state.text); } delete this.reject; this.close(); } render() { return}}复制代码 helloyou can check result in devtool{ const v = (e && e.target && e.target.value) || ''; this.setState({ text: v, }) }} value={this.state.text} >
open 和 close method 在 AsyncModalBase内实现,在AsyncModalDemo内只需要实现handleOk就可以了。这样就复用了open和close两个method。
然后尝试一下HOC:
import React from 'react';import { Modal, Input, Button } from 'antd';const ModalHoc = (modalProps = {}) => DefaultComponent => class extends React.Component { state = { visible: false, } open = () => { this.setState({ visible: true, }); return new Promise((resolve, reject) => { this.resolve = resolve; this.reject = reject; }); } close = () => { this.setState({ visible: false, }); if (this.reject && typeof this.reject === 'function') { this.reject('cancel'); } } hanldeResolve = (...params) => { if (typeof this.resolve === 'function') { this.resolve(...params); } delete this.reject; this.close(); } render() { return}}class Demo extends React.Component { state = { text: '', } handleOk = () => { this.props.hanldeResolve(this.state.text) } render() { return }}export default ModalHoc()(Demo);复制代码helloyou can check result in devtool{ const v = (e && e.target && e.target.value) || ''; this.setState({ text: v, }) }} value={this.state.text} >
很容易封装出了一个ModalHOC,同时做到了比较好的代码复用和解耦。这样,我们只需要实现modal内的业务逻辑,如表单、选择器……。外层的调用方式还是保持一致。
对于使用Vue的同学,也可以封装一个AsyncModal。下面以element-ui/el-dialog为基础做一些改进:
modal_mixin:
export default { data() { return { visible: false, } }, watch: { visible(val) { if (!val) { if (typeof this.handleClear === 'function') { this.handleClear(); } if (this.reject) { this.reject('放弃操作'); } } } }, methods: { open(...params) { this.visible = true; if (typeof this.handleInit === 'function') { this.handleInit(...params); } return new Promise((resolve, reject) => { this.resolve = resolve; this.reject = reject; }); }, handleResolve(...params) { delete this.reject; this.visible = false; if (this.resolve) { this.resolve(...params); } }, close() { this.visible = false; } }}复制代码
async modal:
复制代码 you can check result in devtool取消 确认
调用:
复制代码open modal
使用mixin做代码复用。看起来似乎比react版本更简单一些。