1、React 是如何优化组件更新(React.memo、useMemo、useCallback)
答:React.memo、useMemo和useCallback它们都是 React 提供的优化工具,可以避免不必要的重新渲染,它们可以减少性能开销,提升用户体验。
React.memo是一个高阶组件(HOC),用于包裹函数组件。它会在组件的props没有变化时,跳过重新渲染。- 防止父组件重新渲染时,子组件无变化也跟着渲染的问题。
import React from 'react';
const MemoComp = ({ title }: { title: string }) => {
console.log('子组件触发渲染'); // 只有 title 改变才会触发 该组件的渲染
return <div>{title}</div>;
};
export default React.memo(MemoComp);
默认使用浅比较(shallowEqual)判断 props 是否变化。
- 可以传第二个参数(自定义比较函数):
export default React.memo(MemoComp, (prevProps, nextProps) => {
return prevProps.title === nextProps.title; // 条件成立时,才会不渲染
});
useMemo用于缓存计算结果,只有依赖项变更时才会重新计算,避免重复计算开销大的逻辑。使用场景复杂数据计算、避免每次渲染都重复执行。
const expensiveValue = useMemo(() => {
return computeExpensiveData(data);
}, [data]);
useCallback返回缓存后的函数引用,避免因函数地址变化导致子组件重新渲染,useCallback(fn, deps)等价于useMemo(() => fn, deps)
const handleClick = useCallback(() => {
console.log('点击事件');
}, [count]);
- 若不使用
useCallback,即使函数逻辑不变,函数地址每次 render 都会变,可能导致 React.memo 的子组件重新渲染。
// 父组件没次更新都会重新构建整颗Fiber树,const handleClick 地址会改变,如果子组件Child使用了React.memo,默认潜比较 onClick 这个 props 都会发生更变,React.memo功能就相当于失效了
function Parent() {
const handleClick = () => {
console.log('点击了');
};
}
<Child onClick={handleClick} />;
扩展: 高阶组件(HOC):React 中的一个设计模式;就是一个函数,接收一个组件作为参数,并返回一个新的组件。
2、useState 与 useEffect 的执行顺序?
答:每次组件渲染时的执行顺序:
- 执行组件函数体:
- 执行 useState:用于声明 state,记录当前值。
- 执行同步代码:如
console.log('组件渲染')
- 浏览器更新 DOM
- 执行 useEffect 中的回调函数(异步)
注:
useEffect是异步的,因为 React 要保证组件先“渲染”(即更新 DOM)完毕,再执行副作用(比如发请求、添加事件监听等);否则副作用可能会尚未更新的 DOM操作。- useState 是同步执行,但更新状态(setState)后触发的重新渲染是异步批处理的;
- useEffect 默认是异步的副作用钩子;若需要同步执行副作用(如操作 DOM),可以使用 useLayoutEffect;
function MyComponent() {
const [count, setCount] = useState(0); // ① useState 立即执行(定义状态)
console.log('组件渲染'); // ② 同步代码执行
useEffect(() => {
console.log('useEffect 执行'); // ③ useEffect 在渲染后异步执行(DOM 更新后)
}, [count]);
return <div>{count}</div>;
}
setState是同步调用 + 异步更新的机制,同步调用指的是setState()会立刻执行,但更新不会马上生效,React 会进行 批量处理和调度,在合适的时机统一更新 state 并触发重新渲染。
3、useEffect 第二个参数 deps 的作用?
答:useEffect的第二个参数deps(依赖项数组)是用于控制副作用执行时机的关键配置。它会告诉 React当哪些依赖发生变化时,才需要重新执行 effect 中的副作用函数。
- 无依赖项(只执行一次):仅在组件初次挂载时执行,后续更新不会重新执行;常用于初始化请求、注册事件监听器等。
useEffect(() => {
console.log('组件挂载时执行');
}, []);
- 有依赖项(按需执行):只有 count 发生变化时,才会重新执行,React 会用浅比较判断依赖是否变了,可以多个依赖:
[a, b, c]
useEffect(() => {
console.log('count 变化时执行');
}, [count]);
- 不写依赖项(每次渲染都执行),不推荐,容易导致性能问题或死循环
useEffect(() => {
console.log('每次 render 都执行');
});
- 清理副作用(return 的函数)
useEffect(() => {
const timer = setInterval(() => {
console.log('定时器...');
}, 1000);
return () => {
clearInterval(timer); // 清理定时器
};
}, []);