react-call
@yKicchan
ReactComponent を 手続き的に呼び出せる
window.confirm
const message = 'Sure?' const yes = window.confirm(message) if (yes) thanosSnap() //
const props = { message: 'Sure?' } const yes = await Confirm.call(props) if (yes) thanosSnap() //
import { createCallable } from 'react-call' interface Props { message: string } type Response = boolean export const Confirm = createCallable<Props, Response>( ({ call, message }) => ( <div role="dialog"> <p>{message}</p> <button onClick={() => call.end(true)}>Yes</button> <button onClick={() => call.end(false)}>No</button> </div> ) )
createCallable で wrap する
createCallable
call オブジェクトが props に追加される
call
props
call.end(response) で結果を返す
call.end(response)
Root
import { Confirm } from './path/to/Confirm'; export const App = () => ( <> <Confirm.Root /> {/* 略 */} </> )
createCallable により Root が追加されるので使う
Root は Confirm に対して一つだけ配置する
Confirm
配置場所は最上位である必要はない
import { Confirm } from './path/to/Confirm'; export const Anywhere = () => { const onClick = async () => { const res = await Confirm.call({ message: 'Sure?' }); if (res) thanosSnap(); } return <button onClick={onClick}>Snap</button> }
利用したい場所で call するだけ
配置した Root 以下の必要がある
const [open, setOpen] = useState(false); return ( <> <Button onClick={() => setOpen(true)}>open</Button> <Modal open={open} onClose={() => setOpen(false)}> <!-- 略 --> </Modal> </> );
こういう useState が不要になり責務がスッキリする
useState
const promise = Alert.call({ message: 'Starting operation...' }) await asyncOperation() Alert.update(promise, { message: 'Completed!' }) setTimeout(() => Alert.end(promise), 3000)
promise を渡して特定の呼び出しに対して更新/終了できる
promise
// すべての Confirm を終了させる Confirm.end(false) // すべての Alert を更新する Alert.update({ message: 'Completed!' })
一度に呼び出す数が一つだけの場合もこの方がスッキリする
interface Props { i: number; } export const Modal = createCallable<Props>( ({ call, i }) => ( <div role="dialog"> <p>{i}個目のモーダル</p> <button onClick={() => Modal.call({ i: i + 1 })}>Call {i + 1}</button> <button onClick={() => call.end()}>Close</button> </div> ) );
ネスト結果の取り回しは実装が必要
const UNMOUNTING_DELAY = 500 export const Modal = createCallable( ({ call }) => ( <div className={clsx({ 'exit-animation': call.ended, })}> {/* 略 */} </div> ), UNMOUNTING_DELAY )
.exit-animation { opacity: 0; transition: opacity .5s; }
createCallable の第二引数で unmount までの時間を指定
call.ended で終了判定ができる
call.ended
react-dom や WebAPI に依存していないため動作環境を選ばない
react-dom
WebAPI
<Confirm.Root />
Root の設置(セットアップ)は SSR に対応
const response = await Confirm.call();
call はクライアントサイドのみで利用可能
Promise