はじめに
多少マニアックですが、useState/useReducerでのbail outに関するメモです。少し前の話になりますが、 https://github.com/facebook/react/pull/14569 のプルリクで入ったものです。
どういうことか
const MyComponent = () => {
const [person, setPerson] = useState({
name: 'qiita',
age: 8,
});
// ...
};
というようなコンポーネントがあるときに、useState(person)
として同じオブジェクトを渡した場合に再レンダリングされないということになります。当たり前といえば、当たり前なので、別に困ることはないですね。
ちなみに「キャンセル」というのは多少語弊があって、そのuseStateに起因するレンダリングが止まるだけであって、他のuseStateやuseContextがあってそれがレンダリングの起因となったり、そもそも親コンポーネントからレンダリングされる場合は止まりません。逆に、止まったら困ることになります。
どういうときに便利か
useStateの場合は利用シーンがあまりないかもしれませんので、useReducerの場合で考えます。ステートを変更しようと思ったけど実は変更がなかったという場合にレンダリングをしないようにできます。
例えば、ageを加算する例は次のようになります。
const personReducer = (state, action) => {
if (action.type === 'SET_NAME') {
return { ...state, name: action.name };
} else if (action.type === 'ADD_AGE') {
const newAge = state.age + action.age;
if (newAge === state.age) {
// bail out
return state;
}
return { ...state, age: newAge };
} else {
throw new Error('no action type found');
}
};
const MyComponent = () => {
const [person, dispatch] = useReducer(personReducer, {
name: 'qiita',
age: 8,
});
// ...
};
人工的な例なので便利さは伝わりにくいかもしれませんが、できることは伝わるのではないでしょうか。
おわりに
"bail out"という用語の理解には苦労しました。React内部の話なので、利用者向けのドキュメントにはどこにも載っておらず。React hooksの知見やベストプラクティスはこれから溜まっていくのではないかと期待しています。