为何组件重渲染时useState初始值函数getSth会重复调用?
问题
点击按钮修改isSth状态导致组件重渲染时,明明未改变randomValue状态,函数getSth(可通过console.log观测到)为何每次都会被调用?
示例代码1
import React, { useState } from 'react'; function getSth() { console.log('init'); return Math.random(); } function Home() { const [randomValue, setRandomValue] = useState(getSth()); const [isSth, setIsSth] = useState(false); console.log(randomValue); const onClick = (event: React.MouseEvent<HTMLButtonElement>) => { setIsSth((prev) => !prev); }; return ( <button type="button" onClick={onClick}> Click me </button> ); } export default Home;
而采用如下写法时,getSth仅被调用一次?
示例代码2
const [randomValue, setRandomValue] = useState(0); useEffect(() => { getSth(); }, []);
原因解析
示例1中getSth重复执行的本质
React组件每次重渲染时,整个组件函数都会从头到尾重新执行一遍。你在useState里写的是useState(getSth()),这意味着组件每次执行时,都会先调用getSth()得到返回值,再把这个值传给useState。
虽然useState内部会记住第一次的初始值,后续重渲染时不会更新randomValue,但getSth()的调用是在useState执行前就发生的——组件每渲染一次,这个函数调用就会触发一次,所以你能看到console.log('init')每次都输出。
换个直白的说法:useState(getSth())等价于先执行const temp = getSth(),再执行useState(temp),组件重渲染时这个temp的赋值步骤会重复执行。
示例2中getSth仅执行一次的原因
useEffect的第二个参数是依赖数组,当传入空数组[]时,这个useEffect只会在组件首次挂载完成后执行一次。后续组件重渲染时,因为依赖数组没有任何变化,React不会再触发这个useEffect,所以里面的getSth只会被调用一次。
更优的写法:useState惰性初始值
如果你的需求是让getSth只作为useState的初始值执行一次,其实不用useEffect,直接把getSth作为函数传给useState即可:
const [randomValue, setRandomValue] = useState(getSth);
这种写法下,React只会在组件首次渲染时调用getSth获取初始值,后续重渲染时不会再执行该函数,这是React官方推荐的惰性初始值用法,专门用来处理初始值计算开销较大的场景。
内容的提问来源于stack exchange,提问作者Unlucky

