5
1

More than 5 years have passed since last update.

アプリケーション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
  }
}
5
1
0

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
  3. You can use dark theme
What you can do with signing up
5
1