アプリケーションUIには、画面を覆い被しユーザー操作を限定する global UI があります。Modal がその代表的なもので、管轄する Queue をアプリケーションで1つ構えておくと有用です。2重表示に悩む事なく、表示待ち行列を操作したり、要件拡張の際に柔軟に対応することが出来ます。
code: github / $ yarn 1223
文字入力で動的に Modal を登録、再生で登録済みのモーダルを順次表示するサンプルを紹介します。
useModalQueue
Queue を扱う Custom Hooks の内訳です。
const useModalQueue = () => {
const [state, update] = useState<State>(...)
const renderModal = useMemo(...) // 表示する QueueItem の参照
const handleRegisterModal = useCallback(...) // QueueItem の登録
const handlePlayQueue = useCallback(...) // 再生モードのスタート
return {
state,
renderModal,
handleRegisterModal,
handlePlayQueue
}
}
Queue に ModalItem を追加するハンドラです。form 要素の submit にこの処理をバインドします。registerModal
関数では、配列に JSX.Element を追加しています。
const handleRegisterModal = useCallback(
(value: string) => {
const handleClose = () => {
update(_state => {
const items = [..._state.items]
items.shift()
const isOpen = items.length !== 0
return { ..._state, items, isOpen }
})
}
update(_state => {
const newItem = registerModal({ // JSX.Element の生成
createdAt: new Date(),
message: value,
handleClose
})
const items = [..._state.items, newItem]
return { ..._state, items }
})
},
[]
)
こちらが生成関数です。コンポーネントをマウントせずに要素を配列に追加します。
const registerModal = (props: Props) => (
<Modal
createdAt={props.createdAt}
handleClose={props.handleClose}
message={props.message}
bgColor={'rgba(0,0,0,.5)'}
/>
)
この registerModal
関数では、同じ Component を返しています。要件に応じて様々な種別の Modal を return すると良いでしょう。
Queue の再生
handlePlayQueue
が発火されると、登録された QueueItem の先頭要素から再生が始まります。renderModal
は、useModalQueue
Custom Hooks から得られる、QueueItem の先頭要素参照です。
const View = (props: Props) => {
const {
state,
renderModal,
handleRegisterModal,
handlePlayQueue
} = useModalQueue()
const isEnablePlayQueue = useMemo(
() => state.items.length !== 0,
[state.items]
)
return (
<div className={props.className}>
<Input handleSubmit={handleRegisterModal} />
<PlayModalQueue
handlePlay={handlePlayQueue}
queueCount={state.items.length}
isEnablePlayQueue={isEnablePlayQueue}
/>
{state.isOpen && renderModal} // here
</div>
)
}
あらかじめ registerModal
で確保されたコンポーネント配列の先頭を返します。先頭以外にも要件に応じて、次に表示するべきモーダルを自由に選ぶことが出来ますね。
const renderModal = useMemo(() => state.items[0], [
state.items
])
useModal
こちらは Modalコンポーネントで利用している Custom Hooks です。表示された時のアニメーションやスタイルを取り扱っているのみなので、Queue の処理とは関係ありません。
const useModal = (props: Props) => {
const [state, update] = useState(...)
const options = useMemo(...)
const bgStyle = useMemo(...)
const modalStyle = useMemo(...)
useEffect(...)
return {
bgStyle,
modalStyle
}
}