函数组件的声明方式及差异+React.memo和userCallback区别
1、函数组件的声明方式及差异
普通函数声明
箭头函数声明
使用React.FC类型(TypeScript专用)
interface 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优化
const 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 - 必要时自定义比较函数
基本用法
import React from 'react'; const MyComponent = (props) => {
return <div>{props.content}</div>;
}; // 用 React.memo 包裹组件
export default React.memo(MyComponent);
自定义比较逻辑(高级用法)
如果默认的浅比较不满足需求,可以手动控制比较逻辑:
// 第二个参数是自定义比较函数
export default React.memo(MyComponent, (prevProps, nextProps) => {
// 返回 true 表示 props "相同",阻止渲染
// 返回 false 表示 props "不同",允许渲染
return prevProps.id === nextProps.id;
});
使用场景案例
场景 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包裹,也会因函数引用不同而触发重新渲染。 - 优化目标:
保持函数引用不变,除非依赖项变化,从而减少子组件无效渲染。
问题场景:
// 父组件
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 中函数的引用变化而重新渲染。
- 问题场景:
语法与参数
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
函数组件的声明方式及差异+React.memo和userCallback区别的更多相关文章
- vue组件常用声明方式
一.前言 这是自己重新写的一个,感觉以前的太写了很多不必要的方式 实际当中基本不会用的 所以自己写了一个常用的组件什么方式 更加的通俗易懂 二.代码如下 <!DOCTYPE html> & ...
- React.memo
介绍React.memo之前,先了解一下React.Component和React.PureComponent. React.Component React.Component是基于ES6 class ...
- React组件复用的方式
React组件复用的方式 现前端的工程化越发重要,虽然使用Ctrl+C与Ctrl+V同样能够完成需求,但是一旦面临修改那就是一项庞大的任务,于是减少代码的拷贝,增加封装复用能力,实现可维护.可复用的代 ...
- react hooks 如何自定义组件(react函数组件的封装)
前言 这里写一下如何封装可复用组件.首先技术栈 react hooks + props-type + jsx封装纯函数组件.类组件和typeScript在这不做讨论,大家别白跑一趟. 接下来会说一下封 ...
- React 函数组件
React 函数组件 1.定义方式 React 函数组件是指使用函数方法定义的组件. 定义方式:与函数的定义方式相同,需要将内容 return 出来,需要注意的是最外层只有一个标签或者使用<&g ...
- JavaScript 函数的两种声明方式
1.函数声明的方式 JavaScript声明函数有两种选择:函数声明法,表达式定义法. 函数声明法 function sum (num1 ,num2){ return num1+num2 } 表达式定 ...
- JS定义函数的两种方式:函数声明和函数表达式
函数声明 关于函数声明的方式,它的一个重要的特性就是函数声明提升(function declaration hoisting),意思是在执行代码之前会先读取函数声明.这就意味着可以把函数声明放在调用它 ...
- javascript函数声明方式
javascript中函数的声明有三种方式: 最常见的函数声明: fun();//可以调用,因为这种声明方式会被浏览器优先加载. function fun() { alert("声明式的函数 ...
- JS中var声明与function声明两种函数声明方式的区别
JS中常见的两种函数声明(statement)方式有这两种: // 函数表达式(function expression) var h = function() { // h } // 函数声明(fun ...
- js中声明函数的三种方式
己亥年 庚午月 癸巳日 宜入宅 忌婚嫁 函数声明方式 声明 : function first(){}: 调用:first() 函数表达式声明方式 声明: var second=function ...
随机推荐
- e-prime3安装
e-prime2.0版本太老,现在安装尝试3.0. 下载 链接: https://pan.baidu.com/s/1XJFDqhoArpIwEf0NpKvoIQ 提取码: h5xk 安装 解压安装包后 ...
- NAT、DANT、SNAT的区别
NAT NAT(Network Address Translation,网络地址转换)是将IP 数据包头中的IP 地址转换为另一个IP 地址的过程.在实际应用中,NAT 主要用于实现私有网络访问公共网 ...
- 从零开始构建一个gradle工程
gradle init --type java-application 首先,确保您已经安装了Java和Gradle.您可以从官方网站下载并按照说明进行安装. 创建一个新的项目文件夹,并进入该文件夹. ...
- 萌新赛 sprintf漏洞
首先是个.git源码源码泄露,用githack回复一下源码 源码 <?php $pass=sprintf("and pass='%s'",addslashes($_GET[' ...
- uniapp横向滚动
scroll-x="true" 出现横向滚动 scroll-with-animation="true" 横向滚动有动画 <scroll-view clas ...
- 认识soui4js(第5篇):使用扩展控件
无论内置控件多么丰富,也不可能满足用户所有需求.总有时候用户需要自己扩展控件. soui4js推荐使用C++来扩展控件,然后通过实现一个js模块来提供js使用. 扩展控件通常涉及到图形上下文的频繁交互 ...
- ISA-L库调研
本文分享自天翼云开发者社区<ISA-L库调研>,作者:何****尔 1.Intel SIMD指令集 SIMD(single instruction multiple data)单指令多数据 ...
- 手把手教你喂养 DeepSeek 本地模型
上篇文章<手把手教你部署 DeepSeek 本地模型>首发是在公众号,但截止目前只有500多人阅读量,而在自己博客园BLOG同步更新的文章热度很高,目前已达到50000+的阅读量,流量是公 ...
- Django setting可以配置什么?
Setting配置信息 注册子应用 # 注意:需要修改Django的全局配置文`settings.py`'''1. 创建子应用 1)在pycharm中创建 python manage.py start ...
- flutter - [02] 基本语法
题记部分 一.注释 ///这是一个注释 //这也是个注释 /* 这还是个注释 */ void main(List<String> args) { print ('你好 dart'); } ...