LoginSignup
1

More than 3 years have passed since last update.

posted at

updated at

Organization

Modal Queue

アプリケーションUIには、画面を覆い被しユーザー操作を限定する global UI があります。Modal がその代表的なもので、管轄する Queue をアプリケーションで1つ構えておくと有用です。2重表示に悩む事なく、表示待ち行列を操作したり、要件拡張の際に柔軟に対応することが出来ます。
code: github / $ yarn 1223

1223-1.jpg 1223.jpg 1223-2.jpg

文字入力で動的に Modal を登録、再生で登録済みのモーダルを順次表示するサンプルを紹介します。

useModalQueue

Queue を扱う Custom Hooks の内訳です。

components/useModalQueue.ts
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 を追加しています。

components/useModalQueue.ts
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 }
    })
  },
  []
)

こちらが生成関数です。コンポーネントをマウントせずに要素を配列に追加します。

components/registerModal.tsx
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 の先頭要素参照です。

components/index.tsx
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 で確保されたコンポーネント配列の先頭を返します。先頭以外にも要件に応じて、次に表示するべきモーダルを自由に選ぶことが出来ますね。

components/useModalQueue.ts
const renderModal = useMemo(() => state.items[0], [
  state.items
])

useModal

こちらは Modalコンポーネントで利用している Custom Hooks です。表示された時のアニメーションやスタイルを取り扱っているのみなので、Queue の処理とは関係ありません。

components/modal/useModal.ts
const useModal = (props: Props) => {
  const [state, update] = useState(...)
  const options = useMemo(...)
  const bgStyle = useMemo(...)
  const modalStyle = useMemo(...)
  useEffect(...)
  return {
    bgStyle,
    modalStyle
  }
}

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
1