React の組み込み hook をおさらいするぜ
useState
状態とその更新関数を提供する。
const [state, setState] = useState(initialState);
setState
で状態を更新すると、コンポーネントが再レンダリングされる。
更新方法は2つある。
- 新しい値を渡す
- 前の値を受け取り新しい値を返す関数を渡す
const Counter = () => {
const [count, setCount] = useState(0);
return (
<>
Count: {count}
{/* 新しい値を渡す */}
<button onClick={() => setCount(0)}>Reset</button>
{/* 前の値を受け取り新しい値を返す関数を渡す */}
<button onClick={() => setCount((prevCount) => prevCount + 1)}>
Increment
</button>
</>
);
};
useEffect
データの購読やタイマーなどの副作用を扱うためのフック。
useEffect(didUpdate);
副作用はレンダリングの完了時に実行される。
副作用でクリーンアップ用関数を返すと、コンポーネントが UI から削除される直前で実行される。
第2引数で副作用が依存する値の配列を渡して、副作用の実行条件を指定できる。
第2引数 | 動作 |
---|---|
省略 | 毎レンダリング時に実行される |
空配列 | 初回レンダリング時のみ実行される |
依存している値の配列 | 初回レンダリングおよび渡した値が変化した場合に実行される |
useEffect(
() => {
const subscription = props.source.subscribe();
return () => {
subscription.unsubscribe();
};
},
[props.source],
);
useContext
コンテクストオブジェクトを受け取り、そのコンテクストの現在値を返す。
const value = useContext(MyContext);
コンテクストの値が変化するたびに再レンダリングされる。
コンテクストの用途としては以下のようなものがある。
- 認証済みユーザ
- テーマ
- 優先言語
useReducer
const [state, dispatch] = useReducer(reducer, initialArg, init);
useState
の親戚。
useState
よりも複雑なロジックが絡んだ状態を宣言するのに適している。
const initialState = {count: 0};
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</>
);
}
useReducer
を用いることで、ステート更新関数をステート非依存にすることを強制できます。
あるステートを更新するときに別のステートを見る必要が発生するかもしれません。そのときがuseReducer
導入のサインです。ぜひリファクタリングしてuseReducer
を導入しましょう。
useReducerの本質:良いパフォーマンスのためのロジックとコンポーネント設計 - Qiita
useCallback
メモ化されたコールバックを返す。
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
第2引数に依存する値の配列を渡す。
関数は依存配列の要素のいずれかが変化した場合のみ変化する。
useMemo
メモ化された値を返す。
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
レンダリング毎に高価な計算が実行されるのを避けることができる。
useRef
ref オブジェクトを返す。
概念としてはクラスのインスタンス変数のようなもの。
const refContainer = useRef(initialValue);
よくあるユースケースは DOM にアクセスするパターン。
const TextInputWithFocusButton = () => {
const inputEl = useRef(null);
const onButtonClick = () => {
inputEl.current.focus()
}
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
イベントハンドラや副作用内で直接 current
の値を書きかることができ、クラスのインスタンス変数のように使うことができる。
const Timer = () => {
const intervalRef = useRef()
useEffect(() => {
const id = setInterval(() => { /* ... */ });
intervalRef.current = id;
return () => {
clearInterval(intervalRef.id);
};
});
}
current
プロパティを書き換えても再レンダリングは発生しない。
useImperativeHandle
ref
Props を提供するコンポーネントで、親コンポーネントに渡されるインスタンス値をカスタマイズするのに使用する。
useImperativeHandle(ref, createHandle, [deps]);
以下の例だと、親コンポーネントで <FancyInput ref={inputRef} />
をレンダーすると inputRef.current.focus()
を呼べる。
function FancyInput(props, ref) {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
return <input ref={inputRef} ... />;
}
FancyInput = forwardRef(FancyInput);
useLayoutEffect
useEffect
の親戚。
useEffect
がレンダリング完了後に非同期的に実行されるのに対して、useLayoutEffect
はレンダリング中に同期的に実行される。
視覚的に影響を与える副作用を useEffect
で行うとちらつきが発生するので、それを回避する際に使える。
useDebugValue
React DevTools でカスタムフックのラベルを表示できる。
useDebugValue(value);
第2引数でフォーマット用関数を指定できる。
useDebugValue(date, date => date.toDateString());
useDeferredValue
特定の状態の更新の優先度が低いことを React に通知するために使える。
const deferredValue = useDeferredValue(value);
以下の例では、優先度の高い SearchInput
の再レンダリングが、優先度の低い SearchSuggestions
の再レンダリングによって阻害されないように、deferredQuery
の更新を遅延している。
function Typeahead() {
const query = useSearchQuery('');
const deferredQuery = useDeferredValue(query);
const suggestions = useMemo(() =>
<SearchSuggestions query={deferredQuery} />,
[deferredQuery]
);
return (
<>
<SearchInput query={query} />
<Suspense fallback="Loading results...">
{suggestions}
</Suspense>
</>
);
}
useTransition
useDeferredValue
の親戚。
特定の状態の更新の優先度が低いことを React に通知するために使える。
const [isPending, startTransition] = useTransition();
startTransition
で状態の更新をラップすることで、その状態の更新はいい感じに遅延される。
更新待ちの状態は isPending
という Boolean 変数で参照できる。
function App() {
const [isPending, startTransition] = useTransition();
const [count, setCount] = useState(0);
function handleClick() {
startTransition(() => {
setCount(c => c + 1);
})
}
return (
<div>
{isPending && <Spinner />}
<button onClick={handleClick}>{count}</button>
</div>
);
}
useDeferredValue
が状態の値をラップするのに対し、useTransition
は状態の更新コードをラップするという違いがある。
違いについては useTransition() vs useDeferredValue | React 18 - YouTube が分かりやすい。
useId
ハイドレーション時の不整合を防ぎつつサーバとクライアント間で安定な一意 ID を作成する。
const id = useId();
id
を必要としている要素に直接渡すのが一般的な使い方。
const Checkbox = () => {
const id = useId()
return (
<>
<label htmlFor={id}>Do you like React?</label>
<input id={id} type="checkbox" name="react"/>
</>
)
}
複数の ID を使う場合は同じ id
に sufix をつけて使う。
const NameFields = () => {
const id = useId();
return (
<div>
<label htmlFor={id + '-firstName'}>First Name</label>
<div>
<input id={id + '-firstName'} type="text" />
</div>
<label htmlFor={id + '-lastName'}>Last Name</label>
<div>
<input id={id + '-lastName'} type="text" />
</div>
</div>
);
}