Recoilを使用したカスタムトーストフックの作成


緒論


トーストって何?


トーストは特定のイベントに反応し、簡単なフィードバックを短いテキストでUIを提供します.

トーストの実施方法


次のように、特定のイベントの発生状況をステータスとして管理することで、トーストをオフセットできます.
const [isError, setIsError] = useState(false);
...
{ isError && <Toast/> }
スクリーンの移動中、トーストは懸濁しなければなりません.
トーストが浮遊する時間の後にアンロードする必要がありますが、トーストが再浮遊する必要がある場合は、アンロードする必要があるプロセスが面倒になります.
グローバル変数としてトーストを管理し,トーストを含む空間もルータの上部に置く.

本稿で紹介する内容


本論文では,上記の考慮事項に基づいて,Recoilおよびstyled componentを用いてトースト容器アセンブリを実現し,トーストを用いたhookを作製する論理について議論した.

Related Pull Request


https://github.com/Neogasogaeseo/Naega-Web/pull/78

本題


Recoil Atomの定義

@stores/toast
import { atom } from 'recoil';

export interface Toast {
  id?: string;
  content: string;
  duration?: number;
  bottom?: number;
}

export const toastState = atom<Toast[]>({
  key: 'toastState',
  default: [],
});
画面に表示されているすべてのトーストを保存する状態を作りました.
各ロールは次のようになります.
  • id:リストからトーストを削除する際に表示するid
  • content:画面表示
  • duration:画面表示時間(ミリ秒)
  • bottom:ボトムのpx
  • useToast hookの作成

    @hooks/useToast
    import { Toast, toastState } from '@stores/toast';
    import { getRandomID } from '@utils/etc';
    import { useRecoilState } from 'recoil';
    
    export function useToast() {
      const [toasts, setToasts] = useRecoilState(toastState);
    
      const removeToast = (toastID: Toast['id']) =>
        setToasts((prev) => prev.filter((toast) => toast.id !== toastID));
    
      const fireToast = (toast: Toast) => {
        setToasts((prev) => [...prev, { ...toast, id: getRandomID() }]);
        setTimeout(() => removeToast(toast.id), 600 + (toast.duration ?? 1000));
      };
    
      return { toasts, fireToast };
    }
    このときidを設定する関数は以下のようになります.@utils/etc
    export const getRandomID = () => String(new Date().getTime());
    上で作成したstateを操作するためにhookを作成します.
    toastsは、recoilから受信したコンテンツを返し、toastを追加し、duration後にtoastを削除するfireToastという関数を返します.
    durationに600を加えた理由は、アニメーション表示トーストが消える時間を確保するためだ.

    AST ItemとAST Listコンポーネントの実装

    @common/Toast/Item
    import { Toast } from '@stores/toast';
    import { useEffect, useState } from 'react';
    import { StToastItem } from './style';
    
    function ToastItem(props: Toast) {
      const { content, bottom, duration } = props;
      const [isClosing, setIsClosing] = useState(false);
    
      useEffect(() => {
        const setExistTimeout = setTimeout(() => {
          setIsClosing(true);
          clearTimeout(setExistTimeout);
        }, duration ?? 1000);
      });
    
      return (
        <StToastItem bottom={bottom} isClosing={isClosing}>
          {content}
        </StToastItem>
      );
    }
    
    export default ToastItem;
    toastをアンインストールすると、isClosingのアニメーションが管理され、duration後にtrueに変換されます.
    styled componentはisClosing値を受信し、他のアニメーションを提供します.@common/Toast/Item/style
    import styled from 'styled-components';
    import { ANIMATION } from '@styles/common/animation';
    
    export const StToastItem = styled.div<{ bottom?: number; isClosing: boolean }>`
      position: absolute;
      ...
      bottom: ${({ bottom }) => bottom ?? 26}px;
      animation: 0.3s forwards
        ${({ isClosing }) => (isClosing ? ANIMATION.FADE_OUT : ANIMATION.FADE_IN)};
    `;
    @common/Toast/List
    import { toastState } from '@stores/toast';
    import { useRecoilValue } from 'recoil';
    import ToastItem from '../Item';
    import { StToastList } from './style';
    
    function ToastList() {
      const toasts = useRecoilValue(toastState);
      return (
        <StToastList>
          {toasts.map((toast) => (
            <ToastItem key={toast.id} {...toast} />
          ))}
        </StToastList>
      );
    }
    
    export default ToastList;
    トーストリストに入れます.
    この場合、いずれのページも一定の形式で表示しなければならないので、位置決めを与えます.@common/Toast/List/style
    import styled from 'styled-components';
    
    export const StToastList = styled.div`
      bottom: 0;
      left: 0;
      position: fixed;
      z-index: 1000;
    `;

    App.インプラントtsx

    App.tsx
    import GlobalStyle from '@styles/global';
    import Router from '@routes/Router';
    import ToastList from '@components/common/Toast/List';
    
    function App() {
      return (
        <>
          <GlobalStyle />
          <ToastList />
          <Router />
        </>
      );
    }
    
    export default App;
    上で実装したToostListを最上位層に入れる.

    n/a.結論


    構成部品での使用

    const { fireToast } = useToast();
    
    fireToast({ content:"안녕" });
    トーストの生成,消失,アニメーションなどのプロセスは素子内で抽象化されるため,外部素子はfireToast関数を呼び出すだけでよい.
    このようにcustom hookやrecoilを使うと、トーストが便利に使えます.