const Component = ({ value }) => {
const ref = useRef()
useEffect(() => {
ref.current = value
}, [value])
return (
<div></div>
)
}const Component = () => {
const ref = useRef()
const fn = () => {}
useLayoutEffect(() => {
ref.current = fn
}, [fn])
return (
<div></div>
)
}
useEffect なしで直接でも良さそうなのになんで useEffect を使ってるんでしょう?
単純に副作用は useEffect の中ですべきだから かもしれません
ですが ref ってこの関数の中だけのもので そのプロパティを書き換えるくらい別にいいんじゃないのって気もしてます
render の処理は 2 回実行されても問題ない処理であるべきと言うのを聞いたりしますが 2 回実行されても問題ないと思います
そう思ってましたが それは最新の値を保持するためという視点であって 過去の値を保持するという視点では問題がありました
ただ普通に動かす限りではほぼ問題にはなりません
const Component = ({ value }) => {
const ref = useRef()
const prev = ref.current
ref.current = value
console.log(prev, ref.current)
return (
<div>{prev}/{ref.current}</div>
)
}
const App = () => {
const [state, up] = useReducer(s => s + 1, 1)
return (
<div>
<button onClick={up}>up</button>
<Component value={state} />
</div>
)
}
App ではボタンを押すとカウントアップして Component では前回の値と今回の値を表示しています
「/1」→「1/2」→「2/3」 という風にボタンを押すごとに表示が変わります
StrictMode を有効にして render 処理を 2 回行うようにします
render(
<React.StrictMode>
<App/>
</React.StrictMode>,
document.getElementById("root")
)
すると 「/1」→「2/2」→「3/3」 という風に同じ数値になりました
1 回目の render 処理で ref.current が更新され 2 回目では前回の値がすでに更新後のものになっているためです
console.log をみると↓のようになっています
undefined 1
undefined 1
1 2
2 2
2 3
3 3
ちなみに React 17 では console.log が上書きされ 2 回目の render 処理中のログは出力されません
React のロードより前に console.log を別名で保存して そっちを参照するような一手間が必要です
このせいで変な挙動に見えて嫌だったのですが 18 では毎回表示されるようになってます
実際に 2 回 render 処理が発生することはほぼ無いと思いますし たしか 18 のコンカレントモード用だったと思うのでこれを使わないことにはそもそも発生しなかったと思います
しかし 2 回呼び出しで問題が発生する可能性はありましたし 同じ関数内のみといっても前後の呼び出しで共有する値になるので副作用と考えて とりあえず useEffect にしておくのが無難なのかもです