1、函数组件的声明方式及差异
-
普通函数声明
-
箭头函数声明
-
使用React.FC类型(TypeScript专用)
reactinterface Props { content: string } // 写法一 const MyComponent: React.FC<Props> = (props) => { return <div>{props.content}</div> } // 写法二, FC 是 React.FC 的简写别名,二者完全等价。 import { FC } from 'react' const MyComponent: FC<Props> = (props) => { return <div>{props.content}</div> }
-
使用React.memo优化
reactconst MemoizedComponent = React.memo(function MyComponent(props) { return <div>{props.content}</div>; });
2、React.memo和userCallback
React.memo
-
介绍及使用时机
React.memo
是什么:一个用于函数组件的性能优化工具,通过浅比较 props 避免无效渲染。何时使用:
- 组件渲染成本高
- 父组件频繁更新但子组件 props 不变
- 需要精细化控制渲染逻辑
如何正确使用:
- 对简单 props 使用默认浅比较
- 对复杂 props 结合
useMemo
/useCallback
- 必要时自定义比较函数
-
基本用法
reactimport React from 'react'; const MyComponent = (props) => { return <div>{props.content}</div>; }; // 用 React.memo 包裹组件 export default React.memo(MyComponent);
-
自定义比较逻辑(高级用法)
如果默认的浅比较不满足需求,可以手动控制比较逻辑:
react// 第二个参数是自定义比较函数 export default React.memo(MyComponent, (prevProps, nextProps) => { // 返回 true 表示 props "相同",阻止渲染 // 返回 false 表示 props "不同",允许渲染 return prevProps.id === nextProps.id; });
-
使用场景案例
react场景 1:父组件频繁更新,但子组件不需要 // 父组件 const Parent = () => { const [count, setCount] = useState(0); return ( <div> <button onClick={() => setCount(count + 1)}>点击 {count}</button> {/* 即使 count 变化,Child 的 props 未变化 */} <Child title="静态内容" /> </div> ); }; // 子组件用 React.memo 包裹 const Child = React.memo(({ title }) => { console.log('子组件渲染'); // 只有 title 变化时才会打印 return <div>{title}</div>; }); 场景 2:复杂数据渲染 // 大数据表格组件 const DataTable = React.memo(({ data }) => { // 假设 data 有 10,000 行数据 return ( <table> {data.map(row => ( <tr key={row.id}><td>{row.value}</td></tr> ))} </table> ); });
useCallback
-
核心作用
useCallback
是 React 的一个性能优化 Hook,用于 缓存函数引用 。它的核心价值是 避免父组件重新渲染时,因函数引用变化导致子组件无效渲染。
解决什么问题?
- 问题场景 :
当父组件传递一个函数给子组件时,若父组件更新,每次会重新创建该函数。即使子组件用React.memo
包裹,也会因函数引用不同而触发重新渲染。 - 优化目标 :
保持函数引用不变,除非依赖项变化,从而减少子组件无效渲染。
问题场景:
react
// 父组件
const Parent = () => {
const [count, setCount] = useState(0);
// 每次 Parent 渲染都会创建新的 handleClick 函数
const handleClick = () => {
console.log('点击事件');
};
return (
<div>
<button onClick={() => setCount(count + 1)}>触发渲染(当前:{count})</button>
{/* 即使 Child 用 React.memo 包裹,仍会重新渲染 */}
<Child onClick={handleClick} />
</div>
);
};
// 子组件(用 React.memo 优化)
const Child = React.memo(({ onClick }) => {
console.log('子组件渲染'); // 父组件每次更新都会触发此日志
return <button onClick={onClick}>子组件按钮</button>;
});
// 问题:父组件每次更新(如 count 变化)时,handleClick 会被重新创建,导致传递给子组件的 onClick 引用不同。
// 结果:即使子组件用 React.memo 包裹,也会因 props 中函数的引用变化而重新渲染。
语法与参数
react
const memoizedFn = useCallback(
() => { /* 函数逻辑 */ },
[dep1, dep2] // 依赖项数组
);
// 参数 1:需要缓存的函数。
// 参数 2:依赖项数组(类似 useEffect 的依赖)。
// 返回值:一个记忆化的函数引用(依赖不变时引用不变)。
// ✅ 正确:依赖项包含 count,避免形成闭包并解决
const handleClick = useCallback(() => {
console.log(count);
}, [count]);
区别
useCallback |
useMemo |
|
---|---|---|
缓存目标 | 缓存函数本身 | 缓存函数的计算结果(值/对象) |
返回值 | 函数引用 | 计算结果 |
等效写法 | useMemo(() => fn, deps) |
useMemo(() => value, deps) |
2025/04/15