React17和18的区别
1、新的根节点API,18采用createRoot 替代ReactDOM.render,并引入并发模式
2、新增了新的hooks,比如useId
3、支持中断渲染,优先处理高优先级任务(如用户输入),提升流畅度
4、增加更多的自动批处理,17是只能在react合成事件进行批处理,对于Promise/setTimeout这类是无法自动批处理的
React Hooks 的实现原理是什么?
- Hooks 通过链表结构存储状态
- 每个组件有对应的"记忆单元格"列表
- `useState`/`useEffect` 等 Hook 调用时,会读取/写入当前组件的单元格
- 必须保证 Hooks 的调用顺序稳定(不能条件调用)
- 依赖于 React 的渲染调度机制
React中 useEffect和use LayoutEffect的区别是什么
useEffect是在浏览器绘制之后执行,是异步操作;不会阻塞渲染
useLayoutEffect是在浏览器绘制之前执行,是同步操作,会阻塞渲染
使用场景
useEffect:
1、数据获取2、事件监听/移除
3、不需要及时更新DOM副作用
useLayoutEffect:
1、需要同步或者获取修改后的DOM布局,如尺寸、滚动距离
2、避免视觉闪烁,如动态调整元素样式后再让用户看到
React setState 第二个参数的作用
setState 的第二个参数是一个 可选的回调函数,它会在 状态更新完成且组件重新渲染后执行。这个特性适用于需要在状态更新后立即操作 DOM 或执行其他逻辑的场景。
React闭包陷阱
在react中,由于js闭包特性,函数或者回调访问仍是创建时的状态;而不是最新值;
1. 本质原因
JavaScript 闭包特性:函数会捕获并保留其创建时的作用域变量(即使外部组件已重新渲染)。
React 函数组件特性:每次渲染都是全新的函数调用,所有局部变量(包括 state、props)都会重新创建。
2. 典型场景
useEffect / useCallback / useMemo:依赖数组不完整时,闭包捕获过期值。
异步操作(setTimeout、事件监听):回调函数访问的是定义时的状态快照,而非最新值。
传递给子组件的回调:子组件可能持有旧闭包,导致访问的父组件状态不更新。
比如:
为什么会出现闭包陷阱?
函数组件每次渲染都会重新执行,所有局部变量都会重新创建
Hooks 的依赖数组决定了何时重新创建回调或effect
JavaScript闭包会捕获函数创建时的变量值
如何避免闭包陷阱问题
正确声明依赖:确保所有依赖都包含在依赖数组中
使用函数式更新:当新状态依赖旧状态时
合理使用ref:需要访问最新值但不希望触发重新渲染时
考虑使用useReducer:对于复杂状态逻辑
使用useEvent提案(未来可能):专门解决事件处理函数的闭包问题
React类组件生命周期
挂载阶段:
constructor → getDerivedStateFromProps → render → componentDidMount
更新阶段:
getDerivedStateFromProps → shouldComponentUpdate → render → getSnapshotBeforeUpdate → componentDidUpdate
卸载阶段:
componentWillUnmount
错误处理:
getDerivedStateFromError / componentDidCatch
shouldComponentUpdate 有什么作用?
核心作用:
性能优化:避免不必要的渲染,减少虚拟 DOM 比较和真实 DOM 操作
精确控制:当你知道某些 props/state 变化不需要更新时手动阻止渲染
避免子组件连带更新:父组件更新时控制子组件是否跟随更新
注意:
shouldComponentUpdate 返回 false 时,componentWillUpdate、render 和 componentDidUpdate 将不会被调用
useEffect 的依赖数组如何工作?
空数组 []: 只在组件挂载和卸载时执行
有依赖项: 依赖项变化时执行
无依赖数组: 每次渲染后都执行