useStateのdocsを読む
これ
Overview
useState is a React Hook that lets you add a state variable to your component.
皆さんご存知ですね
Reference
Parameters
関数を渡すときは、Callせずにpureなinitializer functionそのものを渡す
const [todos, setTodos] = useState(createInitialTodos);
Returns
The set function that lets you update the state to a different value and trigger a re-render.
地味に大事なところ。
Caveats
useState is a Hook, so you can only call it at the top level of your component or your own Hooks.
HookがどうしてTopレベルでしか呼べないのか、use
がどうして条件分岐の中でも呼べるのか、しっかり説明できないかも。purityが保たれてればいいって話に行き着く気はするが...。
set functions, like setSomething(nextState)
Parameters
If you pass a function as nextState, it will be treated as an updater function. It must be pure, should take the pending state as its only argument, and should return the next state. React will put your updater function in a queue and re-render your component. During the next render, React will calculate the next state by applying all of the queued updaters to the previous state.
re-renderが引き起こされてから、queueに入ってるupdater functionが計算されるってこと?
Caveats
The set function only updates the state variable for the next render. If you read the state variable after calling the set function, you will still get the old value that was on the screen before your call.
例のあれか?
If the new value you provide is identical to the current state, as determined by an Object.is comparison, React will skip re-rendering the component and its children.
新しいstate variableが古いそれとObject.is
で比較して同じであればre-renderされない
React batches state updates. It updates the screen after all the event handlers have run and have called their set functions. This prevents multiple re-renders during a single event. In the rare case that you need to force React to update the screen earlier, for example to access the DOM, you can use flushSync.
setStateはバッチ処理されるので、逐次re-renderしたいときはflushSync
を使う
Object.js
を確認
console.log(Object.is('1', 1));
// Expected output: false
console.log(Object.is(NaN, NaN));
// Expected output: true
console.log(Object.is(-0, 0));
// Expected output: false
const obj = {};
console.log(Object.is(obj, {}));
// Expected output: false
Object.is() is also not equivalent to the === operator. The only difference between Object.is() and === is in their treatment of signed zeros and NaN values. The === operator (and the == operator) treats the number values -0 and +0 as equal, but treats NaN as not equal to each other.
const isZero = -0 === 0
はtrue
で const isNaN = NaN === NaN
はfalse
const isZero = Object.is(-0, 0)
はfalse
で const isNaN = Object.is(NaN, NaN)
はtrue
これを直感的に理解できるような知識がほしい
Usage
Adding state to a component
React will store the next state, render your component again with the new values, and update the UI.
大事なので再確認
Updating state based on the previous state
ありがちなミス
function handleClick() {
setAge(age + 1); // setAge(42 + 1)
setAge(age + 1); // setAge(42 + 1)
setAge(age + 1); // setAge(42 + 1)
}
こうしましょうねと
function handleClick() {
setAge(a => a + 1); // setAge(42 => 43)
setAge(a => a + 1); // setAge(43 => 44)
setAge(a => a + 1); // setAge(44 => 45)
}
React puts your updater functions in a queue.
queueという表現が度々出てくるが、それについてはここに書いてあるらしい。別スクラップにて。
Is using an updater always preferred?
However, if you do multiple updates within the same event, updaters can be helpful. They’re also helpful if accessing the state variable itself is inconvenient (you might run into this when optimizing re-renders).
If you prefer consistency over slightly more verbose syntax, it’s reasonable to always write an updater if the state you’re setting is calculated from the previous state. If it’s calculated from the previous state of some other state variable, you might want to combine them into one object and use a reducer.
設定する状態が前の状態から計算される場合は、常にアップデータを書くのがreasonableで、それらがパターン化できるならばreducerに押し込もう。
Resetting state with a key
componentに渡すkeyを変更することで、そのcomponentのstateは初期化される。
詳細はこちらにあるらしい。別スクラップにて。
Storing information from previous renders
previous renderingのstate variableを計算に使うために、コンポーネントの中で条件付きsetStateを書くテクがあるらしい。ちょっとこわい。
import { useState } from 'react';
export default function CountLabel({ count }) {
const [prevCount, setPrevCount] = useState(count);
const [trend, setTrend] = useState(null);
if (prevCount !== count) {
setPrevCount(count);
setTrend(count > prevCount ? 'increasing' : 'decreasing');
}
return (
<>
<h1>{count}</h1>
{trend && <p>The count is {trend}</p>}
</>
);
}
Note that if you call a set function while rendering, it must be inside a condition like prevCount !== count, and there must be a call like setPrevCount(count) inside of the condition.
つける条件にも条件がある
This pattern can be hard to understand and is usually best avoided. However, it’s better than updating state in an effect.
むずいから避けたほうがいいけど、useEffectの中でstateを更新するよかマシって書いてある。
Troubleshotingでこちらがおすすめされている
読んでみて
数少ない原理原則を意識して説明がされており、かつその原則について多角的に(繰り返し)説明がされているので、とても丁寧なドキュメントだなあと思う。Dan先生が書いたのかな、まじで上手。
どうしてもpureというキーワードが繰り返し出てくるので、1つ上の記事が気になっている。ベースとして各APIのdocsを全部読みたいというのがあるが、次はこれを読む。