📝
Reactでゼロからトーストを実装してみた
はじめに
今回はReactでトーストを実装しました。トーストのライブラリはいくつもありますが、今回はゼロから実装してみました。完成イメージは以下の通りです。この中から今回実装した部分についてご紹介します。
対象の読者
useState, useContextなどの使い方を把握している方
実装内容
トースト本体は以下のように実装しました。途中に出てくるVisibleコンポーネントはisOpenがtrueの際に<Visible></Visible>
で囲った部分を表示するコンポーネントです。Toastコンポーネントにtextが渡された際はトーストを表示、textが''
の際はトーストが非表示になります。
Toast.tsx
export const Toast: React.FC<IProps> = ({
text,
displayTime,
}) => {
return (
<Visible isOpen={!!text}>
<div
style={{
minHeight: '40px',
width: '400px',
boxSizing: 'border-box',
position: 'absolute',
bottom: '15px',
right: '15px',
display: 'flex',
flexDirection: 'column',
color: Color.WHITE_DARK,
backgroundColor: Color.GREEN_LIGHT,
borderRadius: '4px',
overflow: 'hidden',
zIndex: '11',
}}
>
<div
style={{
margin: '16px',
fontSize: '14px',
whiteSpace: 'pre',
}}
>
{text}
</div>
<style>
{`@keyframes toShort {
0%{
width: 100%;
}
100%{
width: 0%;
}
}`}
</style>
<div
style={{
height: '4px',
backgroundColor: Color.GRAY_LIGHT,
animation: `toShort ${displayTime}ms`,
opacity: '0.9',
}}
/>
</div>
</Visible>
);
};
アニメーション
よくあるトーストのアニメーションで時間経過に合わせて下のバーが短くなっていくものがあります。せっかくなのでそちらのアニメーションも実装しました。高さ4pxのdivタグを配置して、@keyframesで長さを短くしています。背景色と馴染ませるため透明度を0.9にしています。displayTimeで任意の表示時間を設定できます。
Toast.tsxより抜粋
<style>
{`@keyframes toShort {
0%{
width: 100%;
}
100%{
width: 0%;
}
}`}
</style>
<div
style={{
height: '4px',
backgroundColor: Color.GRAY_LIGHT,
animation: `toShort ${displayTime}ms`,
opacity: '0.9',
}}
/>
呼び出し方
トーストを使用する際には以下のToastContextを介して呼び出します。試作段階ではトーストを使用するコンポーネント内でsetToast
の部分を制御していましたが、使用箇所が増えるたびに同じ処理を何度も書くことになるため、Contextを作成してそれらの処理を1つにまとめました。
ToastProvider.tsx
export const ToastProvider: React.FC<IProps> = ({children}) => {
const displayTime = 3500;
const [toast, setToast] = useState({
text: '',
type: ToastType.SUCCESS,
});
const handleToast = (
text: string,
type: ToastType = ToastType.SUCCESS,
): void => {
setToast({
text: text,
type: type,
});
setTimeout(() => {
setToast({
text: '',
type: ToastType.SUCCESS,
});
}, displayTime)
}
return (
<ToastContext.Provider value={{handleToast}}>
<Toast
text={toast.text}
displayTime={toast.type === ToastType.SUCCESS ? displayTime : 5000}
/>
{children}
</ToastContext.Provider>
);
};
export const useToastContext = () => useContext(ToastContext);
おわりに
今回はライブラリを使わずにゼロからトーストを実装しました。業務で使用する際はMUIなどを使用する方がより速く開発を進めることができますが、たまに趣味で作る分には勉強になっていいですね。
Discussion