Reactで難しいのはレンダリングのタイミングだと思うんですが、データを取得してからレンダリングするという当たり前の処理を作るのも工夫が必要です。
関数の純粋性
サーバーからデータを取ってきたり、データを登録したりする処理は「副作用」と呼ばれます。外部に影響を与える処理のことで、アプリを動かすには欠かせない処理です。
一方、「関数は純粋であるべきだ」という考え方があります。関数における処理はその内部で完結し、関数外の変数などに影響を与えてはいけないということです。また、同じ値を渡したら、同じ値が返ってくるということでもあります。
function increment ( val ) {
return val + 1;
}
例えば代入した値に+1した値を返すincrementという関数を用意します。これは1を代入すれば2、2を代入すれば3となり、関数の外に影響を与えていないので「純粋」と言えます。
var a = 1;
function increment2 () {
a = a + 1;
}
一方で、increment2では関数の外で定義された変数aを直接変更しており、純粋とは言えません。このincrement2がいろいろな場所で呼び出されると、グローバル変数であるaの値はどんどん書き換わっていき、意図しないプログラムになってしまう可能性があります。これを避けるため、関数は関数内部で定義した値のみで処理をする、というのが基本になります。
純粋関数の考えは、useReducerでも使うことになるので、しっかり理解しておくことが重要です。
useEffect
しかし、アプリを作るうえで、外部(例えばデータベース)への影響は必ず出てきます。ゲームのセーブデータとか、掲示板の投稿とか、データを保存しておく処理はすべて「副作用」になります。
この副作用のある処理はReact Hooksの一つであるuseEffectに書きましょうということになっています。
const Example = () => {
const [ val, setVal ] = useState(0);
useEffect( () => {
...副作用のある処理
console.log("useEffect")
setVal( prev => prev + 1 );
}, [])
console.log("Render");
return (
<div>{val}</div>
)
}
useEffectの第1引数には副作用のある処理を書いたコールバック関数を代入します。Reactがレンダリングしたあとに、コールバック関数の処理が走ります。この例では、コールバック関数の中でsetValを呼び出しているので、ここで再レンダリングが予約されます。つまり上の例では
- レンダリング(コンソールに「Render」と表示)
- useEffectのコールバック(コンソールに「useEffect」と表示)
- 再レンダリング(コンソールに「Render」と表示)
という流れで処理されます。
useEffectの第2引数は「依存配列」といって、変数が格納された配列を代入します。すると、格納された変数に変更があった場合に、再度useEffectのコールバック関数が処理されることになります。上の例では空配列なので、再レンダリング時にはコールバックは呼ばれません。
このように副作用のある処理を混ぜると、useeffectやuseStateなどによってレンダリングや再レンダリングが走ることになります。最後に表示させたい値はレンダリングのときには存在せず、useEffectによって処理された値を再レンダリング時に使う、というようなことが生じます。stateの値を元に別な値を計算してコンポーネントに渡そうとすると、こうしたレンダリングや再レンダリングの流れを理解していないと、度々エラーが発生してしまいます。
コメント