React Component で作る window.confirm 代替品
window.confirm
はESLint でエラーが出るし、UI が良くない。なので似たような API で扱うことの出来る window.confirm
の代用品を作ってみた。
参考: http://reactkungfu.com/2015/08/beautiful-confirm-window-with-react/
環境: React (16.1.1), React-Bootstrap (0.32.1)
Confirmation Modal
まずはスタイリッシュな Modal Window を作成。Modal が作れるなら別に React-Bootstrap じゃなくても良い。
class ConfirmationModal extends Component { constructor() { super(); this.state = { show: true, }; } abort = () => { this.setState({ show: false }); }; confirm = () => { this.setState({ show: false }); }; render() { const { show } = this.state; const { body, title } = this.props; return ( <div className="static-modal"> <Modal show={show} onHide={this.abort} backdrop> <Modal.Header> <Modal.Title>{title}</Modal.Title> </Modal.Header> <Modal.Body>{body}</Modal.Body> <Modal.Footer> <Button onClick={this.abort}>Cancel</Button> <Button className="button-l" bsStyle="primary" onClick={this.confirm} > Confirm </Button> </Modal.Footer> </Modal> </div> ); } }
confirm
関数で div
を作成してそこに ConfirmationModal
コンポーネントを描画。resolve
と cleanup
を渡す。 Promise
の resolve
/ reject
時に cleanup
関数で DOM
を掃除しないとモーダル作成時に無限に div
が増えていくことになる。
const confirm = ({ body, title }) => { const wrapper = document.body.appendChild(document.createElement('div')); const cleanup = () => { ReactDOM.unmountComponentAtNode(wrapper); return setTimeout(() => wrapper.remove()); }; const promise = new Promise((resolve, reject) => { try { ReactDOM.render( <ConfirmationModal cleanup={cleanup} resolve={resolve} title={title} body={body} />, wrapper ); } catch (e) { cleanup(); reject(e); throw e; } }); return promise; };
ConfirmationModal
コンポーネントの abort
/ confirm
メソッドで真偽値を解決する。
class ConfirmationModal extends Component { constructor() { super(); this.state = { show: true, }; } abort = () => { const { resolve, cleanup } = this.props; this.setState({ show: false }, () => { resolve(false); cleanup(); }); }; confirm = () => { const { resolve, cleanup } = this.props; this.setState({ show: false }, () => { resolve(true); cleanup(); }); }; ... }
使い方
ConfirmationModal
の応答結果 (真偽値) を resolve
しているので、async
/ await
で次のように使うことが出来る:
async function() { const isConfirmed = await confirm({ title: 'CAUTION', body: 'Are you sure?', }); console.log(isConfirmed); // boolean }
HOC
で、これを HOC 化したのがこちら。
// confim.js import { createConfirm } from 'react-confirm-decorator'; import ConfirmationModal from './ConfirmationModal'; const confirm = props => createConfirm(ConfirmationModal, props); export default confirm;
好きな Modal のライブラリを使うことが出来る:
// ConfirmationModal.js import React from 'react'; import Modal from 'react-bootstrap/lib/Modal'; import Button from 'react-bootstrap/lib/Button'; import { setConfirm } from 'react-confirm-decorator'; const ConfirmationModal = ({ show, confirm, abort, title, body }) => ( <div className="static-modal"> <Modal show={show} onHide={abort} backdrop> <Modal.Header> <Modal.Title>{title}</Modal.Title> </Modal.Header> <Modal.Body>{body}</Modal.Body> <Modal.Footer> <Button onClick={abort}>Cancel</Button> <Button className="button-l" bsStyle="primary" onClick={confirm}> Confirm </Button> </Modal.Footer> </Modal> </div> ); export default setConfirm(ConfirmationModal);
import confirm from './confirm'; async function() { const isConfirmed = await confirm({ title: 'CAUTION', body: 'Are you sure?', }); console.log(isConfirmed); // boolean }
react-confirm という似たふるまいのライブラリを途中見つけたけど、やりたかったこと (window.confirm
の代用) とはちょっと違った。