开始
大家好,我是双越老师~
最近春季招聘季节,好多同学找我 1v1 前端面试咨询
,咨询服务包括 1v1 模拟面试环节。对于 React 技术栈的同学,我会问下面这道问题。
我觉得这是一个非常基础的 setState 应用问题,但 80% 的同学却回答错误,甚至他们都了解或者熟悉 React 原理,甚至看过源码。
PS:我正在开发一个 Node 全栈 AIGC 知识库项目 划水AI ,仿 Notion AI 和协同编辑,如果想一起加入项目开发团队,可私信我~
为面试而背原理
React 原理很多人都能说上一些,什么 JSX、虚拟 DOM、diff 算法、fiber、并发模式、合成事件 ......
社区里也满是这种资料,焦虑你我他,卷不完继续卷,为了面试造火箭。完全没有自己的思考和分析。
例如:为何 React 需要 fiber 并发模式这一套复杂东西,而 Vue 不需要呢?Vue 性能也不差呀。React 是故意弄这一套来为难自己吗?------ 当然不是,肯定是有原因的。
不过,这是个题外话,我们还是先看看这个 setState 面试题吧。
题目
如下代码是一个 React 组件。提问:
- 初次渲染这个组件,控制台会打印什么,组件的
<span>{value}</span>
显示什么值? - 然后点击
increase
按钮,控制台会打印什么,组件的<span>{value}</span>
显示什么值?
js
function Demo() {
console.log('hello')
const [value, setValue] = useState(100)
function clickHandler() {
setValue(value + 1)
setValue(value + 1)
console.log('value1 ', value)
setValue((value) => value + 1)
setValue((value) => value + 1)
console.log('value2 ', value)
}
return (
<div>
<span>{value}</span>
<button onClick={clickHandler}>increase</button>
</div>
)
}
初次渲染
这一步大家都知道,最后显示的值是 100
,没有回答错误的。
但是,绝大部分人会忽略掉一开始的 console.log('hello')
完全无视。程序员要严谨,每一行代码都要读,而且题目已经明确提问了"控制台会打印什么"。
经过我的提示以后,大家才意识到原来还要打印 'hello'
------ 但是,'hello'
打印几次呢?
重复渲染
React18 开发环境下,组件渲染时会被执行两次,这是为了更早的验证组件完整的生命周期。生产环境下不会重复渲染。这个知识点很多同学都不知道,虽然他们也是 React 技术栈的。
所以,'hello'
会被打印两次。哪怕是在 useEffect
中,也是会被打印两次
js
function App() {
console.log('hello')
useEffect(() => {
console.log('hello 100')
})
但,这个不是该题目的重点,即便你不知道这个知识点,只说打印一次 'hello'
,我觉得也还可以。继续往下看。
异步更新 state
继续第二步,点击 increase
按钮,value1
和 value2
分别打印什么?大部分人的答案是
value1 101
value2 103
答案当然是错误的,正确答案是 value1
和 value2
全都打印 100
。不管 Vue 还是 React 它们都是异步更新异步渲染的,同步代码中无法获得最新的值。
不过值得肯定的是,大部分知道 setValue(value + 1)
会合并更新,而 setValue(v => v + 1)
不会合并更新。所以最后页面显示的值是 103
还没结束
所有同学到这里都以为结束了,无一例外。
直到我追问:'hello'
还会打印吗?这才引发一少部分同学的思考,他们意识到 React 组件更新了,函数要重新执行。
然后我继续追问:'hello'
会在什么时机打印呢?会在 'value2'
之前还是之后呢?
这又引起了同学的纠结,不知道什么时机,甚至不知道 'hello'
还会在打印几次。于是有了各种答案:
- 在
'value1'
之后打印一次,在'value2'
之后再打印一次 - 在
'value2'
之前打印一次 'hello'
还有打印 3 次,因为之前有 state 合并,不是 4 次- (答案太多我也记不清了)
其实答案很简单:如果不考虑 React18 这个重复渲染,'hello'
打印一次,如果考虑就打印两次。
我觉得这俩答案都可以,因为我们工作中写代码一般会封装在 useEffect
中,不会有这个问题。
分析整个过程
PS:为了简化流程,不考虑 React18 重复渲染的现象。
第一,组件初次渲染
- 执行函数
- 打印
'hello'
- 定义
value
值100
- 定义
clickHandler
函数,但未执行,且不管它(未执行的函数,就当作一个黑盒,不管) - 返回 UI ,值是
100
第二,点击按钮
- 执行
clickHandler
函数 - 执行两步
setState
但异步更新 - 打印
value1 100
- 执行两步
setState
但异步更新 - 打印
value2 100
第三,state 变化触发组件更新
- 执行函数
- 打印
'hello'
- 定义
value
值103
------ 【注意】到这一步才获取到最新的 state - 定义
clickHandler
函数 ------ 是的,重复定义了,如不想重复请用useCallback
- 返回 UI ,值是
103
答案
如下图
和我一起开发 React 全栈项目
学了 React 但没有真实项目实践经验?简历上写的项目太简单?
来跟我一起做一个 React Next.js Node 全栈 AIGC 项目 划水AI,仿 Notioin AI 和协同编辑 。
项目介绍可以看这里 前端转全栈: Next.js + ChatGPT 开发 AIGC 知识库(AI 写作) 欢迎围观~。
有意请私聊我~