はじめに
2022年現在、Reactを使うとなると、hooksを使う頻度が高いかと思います。
ステートの保持やAPI通信などを伴う副作用の注入。
そしてそれらを汎用化したり、処理を切り分けるために実装するカスタムフック。
今回はそのカスタムフック、実はreact-useというライブラリを使えば作らなくていいかもよ。
という話をします。
react-useとは
よく使う汎用的なhooksの処理をライブラリとして提供してくれています。
今回はよく使うものにフォーカスを当てて話しますが、他にも色々あるので、是非Githubをじっと眺めてみてください。
useEffectOnce
通常、useEffectはコンポーネントが再レンダリングされた際も動きますが、それを初回マウント後と初回アンマウント後に一度のみ行いたい場合に使います。
import {useEffectOnce} from 'react-use';
const Demo = () => {
useEffectOnce(() => {
console.log('Running effect once on mount')
return () => {
console.log('Running clean-up of effect on unmount')
}
});
return null;
};
実は、このコードはReactの公式サイトにも記述があります。
もしも副作用とそのクリーンアップを 1 度だけ(マウント時とアンマウント時にのみ)実行したいという場合、空の配列 ([]) を第 2 引数として渡すことができます。こうすることで、あなたの副作用は props や state の値のいずれにも依存していないため再実行する必要が一切ない、ということを React に伝えることができます。これは特別なケースとして処理されているわけではなく、依存配列を普通に処理すればそうなるというだけの話です。
なので、公式通り、実際のコードをみてみるとuseEffectをラップして空配列を依存配列に渡しているだけだったりします。
import { EffectCallback, useEffect } from 'react';
const useEffectOnce = (effect: EffectCallback) => {
useEffect(effect, []);
};
export default useEffectOnce;
これだけのhooksを使う必要があるのか、という考え方もあるかもしれませんが、
- 明示的に一度しか実行されないことがわかる
- テストコードを書く必要がない
という点で良いhooksだなと思います。
useToggle
モーダルを実装する際など、toggle的なステートを管理するのにuseState
やuseReducer
を用いた実装が上がってくる時があります。
でも、useToggle
を使えばシンプルに書けます
import { useToggle } from 'react-use';
const Demo = () => {
const [on, toggle] = useToggle(true);
return (
<div>
<div>{on ? 'ON' : 'OFF'}</div>
<button onClick={toggle}>Toggle</button>
<button onClick={() => toggle(true)}>set ON</button>
<button onClick={() => toggle(false)}>set OFF</button>
</div>
);
};
useAsync
非同期処理ってちょっと書くの面倒だったりするんですよね。
可読性も悪いですし。
useAsync
を使うといいです。async関数を引数に取ることができます。
import {useAsync} from 'react-use';
const Demo = ({url}) => {
const state = useAsync(async () => {
const response = await fetch(url);
const result = await response.text();
return result
}, [url]);
return (
<div>
{state.loading
? <div>Loading...</div>
: state.error
? <div>Error: {state.error.message}</div>
: <div>Value: {state.value}</div>
}
</div>
);
};
useMount
これまたシンプルなhooksですが、マウント時に実行したい関数を渡すことができます。
import {useMount} from 'react-use';
const Demo = () => {
useMount(() => alert('MOUNTED'));
return null;
};
createReducerContext
Contextを用いた実装をしたくなる時があると思いますが、毎回同じようなコードを書いてるな、、と思います。
このcreateReducerContext
では、reducerと初期値を渡せば、そのreducerとProviderを返してくれます。
import { createReducerContext } from 'react-use';
type Action = 'increment' | 'decrement';
const reducer = (state: number, action: Action) => {
switch (action) {
case 'increment':
return state + 1;
case 'decrement':
return state - 1;
default:
throw new Error();
}
};
const [useSharedCounter, SharedCounterProvider] = createReducerContext(reducer, 0);
だいたいContextを使うときは何かしらのステートとそれを更新するdispatch関数を返したいだけだったりするので、スマートにかけていいですね
まとめ
今回は自分で作っちゃいがちなカスタムフック、実はreact-useというライブラリがもうすでに作ってくれてるかもよ
という話をしました。
メンテナンスコストも下がり、テストコードを書く量も減る。是非使ってみてください