当前位置: 首页 > news >正文

React 10

1 修改组件纯粹的练习

export default function StoryTray({ stories }) {stories.push({id: 'create',label: 'Create Story'});return (<ul>{stories.map(story => (<li key={story.id}>{story.label}</li>))}</ul>);
}

这个 StoryTray 组件不是纯粹的,核心问题在于它直接修改了传入的 props.stories 数组,违反了 React 中 “props 是只读的” 原则,会产生不可预测的副作用。

一、组件不纯粹的关键原因

1. 直接修改 props,产生副作用

stories.push(...) 这行代码是在直接修改父组件传入的 props.stories 数组push 方法会改变原数组)。React 明确规定:props 是只读的,组件绝不能修改自身接收的 props。原因是:

  • props 本质上是父组件传递给子组件的数据,子组件修改 props 会导致 “父组件状态与子组件操作不一致”(比如父组件不知道子组件偷偷改了数据)。
  • 若父组件因其他原因重新渲染,传入的 stories 可能被重置,导致子组件的修改丢失;反之,子组件的修改也可能意外影响父组件的其他逻辑(比如父组件中其他使用 stories 的地方)。
2. 同输入可能不同输出,违反确定性

假设父组件传入的 stories 是 [{ id: '1', label: 'Story 1' }]

  • 第一次渲染时,stories.push(...) 会给原数组增加一个元素,组件返回包含 2 个元素的列表。
  • 若父组件重新渲染(比如其他状态变化),再次传入相同的初始 storiespush 会再次执行,数组会变成 3 个元素,导致输出结果不同。

这种 “相同输入却产生不同输出” 的情况,完全违背了纯函数的确定性原则。

二、优化方案:创建新数组,不修改原 props

解决办法是基于原 props.stories 创建一个新数组,在新数组上添加元素,避免修改原 props。优化后的代码如下:

export default function StoryTray({ stories }) {// 创建新数组:复制原 stories 中的元素,再添加新元素(不修改原 props)const allStories = [...stories,  // 复制原数组的所有元素{ id: 'create', label: 'Create Story' }  // 添加新元素];return (<ul>{allStories.map(story => (  // 使用新数组渲染<li key={story.id}>{story.label}</li>))}</ul>);
}

三、优化后组件的纯粹性验证

  1. 无副作用:不再修改原 props.stories,而是通过扩展运算符(...)复制原数组并创建新数组,保证了 props 的只读性,没有影响外部状态。

  2. 输入输出确定:只要传入的 stories 相同,allStories 的计算结果就完全相同,渲染的列表也必然一致,符合 “相同输入 → 相同输出” 的纯函数特性。

  3. 数据流向清晰:子组件仅使用父组件传入的 props,不修改它,避免了父子组件状态冲突,逻辑可预测。

四、关键结论

React 中,props 的只读性是保证组件纯粹性和数据流清晰的核心原则。修改 props 就像 “偷偷修改别人借给你的东西”,会导致整个应用状态混乱。正确的做法是:对于需要基于 props 加工的数据,始终创建新的副本(如新数组、新对象),而非直接修改原 props。这一点在处理数组和对象类型的 props 时尤其重要(因为它们是引用类型,修改会影响原数据)。

2 Javascript的push方法

Array.prototype.push() - JavaScript | MDN

push 是 JavaScript 中数组(Array)的内置方法,用于向数组的末尾添加一个或多个元素,并且会直接修改原数组(这也是它在 React 组件中容易引发问题的原因)。

一、push 方法的基础用法

语法:数组.push(元素1, 元素2, ...)作用:给原数组添加新元素,返回值是添加后数组的长度。

示例:

const fruits = ['苹果', '香蕉'];
// 向数组末尾添加元素
const newLength = fruits.push('橙子', '葡萄');console.log(fruits); // 输出:['苹果', '香蕉', '橙子', '葡萄'](原数组被修改了)
console.log(newLength); // 输出:4(添加后数组的长度)

二、为什么在 React 组件中用 push 会出问题?

因为 push 会直接修改原数组(产生 “副作用”),而 React 要求组件接收的 props 必须是 “只读的”(不能被修改)【非常形象了!!!!!】。

在你之前的 StoryTray 组件中:

// 传入的 props.stories 是一个数组
stories.push(/* 新元素 */); // 直接修改了原数组,违反了 props 只读原则

这会导致父组件中的原数组被意外修改,引发数据不一致的问题(比如父组件再次使用这个数组时,内容已经被子组件篡改了)。

三、React 中正确处理数组的方式

如果需要基于原数组添加元素,应该创建新数组(不修改原数组),常用方法有:

  1. 扩展运算符(...(推荐):

    // 基于原数组创建新数组,并添加新元素
    const newArray = [...原数组, 新元素1, 新元素2];
    
  2. concat 方法

    // concat 会返回新数组,原数组不变
    const newArray = 原数组.concat(新元素1, 新元素2);
    

举例(对应之前的组件优化):

// 原数组:stories
// 正确方式:创建新数组,不修改原数组
const allStories = [...stories, { id: 'create', label: 'Create Story' }];

总结

push 是 JavaScript 数组的原生方法,功能是 “给原数组添加元素并修改它”。但在 React 中,由于 props 必须只读,直接对 props 中的数组使用 push 会导致副作用,破坏组件纯粹性。正确的做法是通过扩展运算符或 concat 等方式创建新数组,既满足需求又不修改原数据,这也是 React 中处理数组的最佳实践。

3 渲染树中的 HTML 标签在哪里

UIView | Apple Developer Documentation

https://learn.microsoft.com/en-us/dotnet/api/system.windows.frameworkelement?view=windowsdesktop-7.0

4 React摘要

一、渲染树

  1. 基本定义树结构常用于表示实体间关系,在 UI 建模中很常见。React 的渲染树专门表示单次渲染过程中 React 组件之间的嵌套关系,比如一个页面由Header组件、Content组件和Footer组件组成,这三个组件在渲染树中就呈现出以页面根组件为父,它们为子的嵌套结构。
  2. 动态变化性由于 React 的条件渲染机制,渲染树在不同渲染过程中可能发生变化。例如一个组件ConditionalComponent,当属性isShowtrue时渲染ChildA组件,为false时渲染ChildB组件,那么在不同属性值下,渲染树的子组件结构就会不同。
  3. 性能调试价值渲染树能帮助识别顶级组件叶子组件
    • 顶级组件:处于渲染树上层的组件,其渲染性能会影响其下所有子组件的渲染。比如一个负责数据获取和全局状态管理的顶级组件,如果其渲染逻辑过于复杂,会导致整个页面的渲染性能下降。
    • 叶子组件:处于渲染树末端的组件,通常会频繁重新渲染(比如展示实时数据的组件)。识别这两类组件,能帮助开发者理解和调试渲染性能问题,比如对频繁渲染的叶子组件做性能优化(如使用React.memo),对影响范围大的顶级组件优化其渲染逻辑。

二、依赖树

  1. 基本定义依赖树表示React 应用程序中的模块依赖关系,即一个模块(或组件)依赖于哪些其他模块。例如一个UserList组件依赖于UserService模块来获取用户数据,UserService又依赖于HttpUtil模块来发送网络请求,这些依赖关系就构成了依赖树。
  2. 构建工具的作用构建工具(如 Webpack、Vite 等)会利用依赖树来捆绑必要的代码以部署应用程序。它会分析依赖树,找出哪些模块是应用运行所必需的,然后将这些模块的代码打包成一个或多个文件,避免打包冗余代码。
  3. 性能优化价值依赖树有助于调试大型捆绑包带来的渲染速度过慢问题,还能发现哪些捆绑代码可以被优化。比如通过依赖树发现某个未使用的第三方库被错误地打包进了应用,就可以将其从依赖中移除,减小捆绑包体积,提升渲染速度;或者发现某些模块被多个地方重复依赖,可通过代码分割等方式优化。
http://www.dtcms.com/a/544536.html

相关文章:

  • 京东方 EV101WXM-N10 工业液晶模组技术摘要
  • Deep End-to-End Alignment and Refinement for Time-of-Flight RGB-D modules复现
  • Java-163 MongoDB 生产安全加固实战:10 分钟完成认证、最小权限、角色详解
  • MinIO 与云原生_现代化对象存储解决方案
  • 【C语言实战(63)】从0到1:51单片机GPIO控制实战秘籍
  • 金仓替代MongoDB:互联网医院聊天脱敏实战
  • 使用 ESLint + Prettier + Husky
  • mongodb备份脚本(单机+副本集)
  • 金仓数据库平替MongoDB全栈安全实战:从文档存储到多模一体化的演进之路
  • 基于k8s环境下mongodb备份恢复实战
  • 申威ky10架构安装MongoDB 4.0.1(rpm包:mongodb-4.0.1-8.ky10.sw_64.rpm)详细步骤
  • 网站建设开发语言和使用工具it培训套路
  • Diffusion Model与视频超分(2):解读字节开源视频增强模型SeedVR2
  • Linux小课堂: 系统硬件资源管理与设备操作指南
  • ROS2核心概念之代码示例一
  • 工厂考勤系统选型参考:如何选出最合适的方案
  • 【前端小站】CSS 样式美学:从基础语法到界面精筑的实战宝典
  • Linux错误(7)接口处于Down状态不通告IPv6地址变更事件
  • 开发避坑指南(67):Maven引入iText7-core依赖失败解决方案
  • 北京南站地图建设公司简介怎么写
  • 镇江网站建设平台江苏企业展厅设计公司
  • 用JetBrains Rider开发C#应用程序指南
  • 快速创建Word箱单(2/2)
  • LangChain4j学习11:模型上下文协议 (MCP)
  • mysql线上主从集群设置
  • RK3568 11.0编译报错ld.lld: error: undefined symbol: rtkbt_cts_info
  • 南京大学LLM开发基础(四)MoE, LoRA, 数的精度 + MLP层实验
  • 机器学习-回归分析概述
  • 企业建设网站的主要作用网站seo搜索引擎优化怎么做
  • 微信小程序可以做电影网站吗安网多少钱