React 开发/学习 记录

本文详细记录了React开发过程中的关键点,包括Vue与React的对比、React的Context、Hooks的使用(如useState和useReducer)、状态管理(如MobX)以及React的生命周期和组件通信。此外,还探讨了React的异步状态更新、路由懒加载、样式引入、错误边界等话题,旨在帮助读者深入理解React开发。
摘要由CSDN通过智能技术生成

Vue 和 React 对比

写了Vue以后,再回头看React,修改state为数组或对象的时候,就会感觉束手束脚,很多JSES语法都不能用,
因为改变了原来的state。 这边需要重新创建一个备份数据,再用 setXxx 进行修改。

React中也有ref,用法和Vue中差不多

Vue 和 React都用到了大量hook,通过use来定义的

Context

Context 使组件向其下方的整个树提供信息。
传递 Context 的方法:
    1.通过 export const MyContext = createContext(defaultValue) 创建并导出 context。
    2.在无论层级多深的任何子组件中,把 context 传递给 useContext(MyContext) Hook 来读取它。
    3.在父组件中把 children 包在 <MyContext.Provider value={
   ...}> 中来提供 context。
Context 会穿过中间的任何组件。
Context 可以让你写出 “较为通用” 的组件。
在使用 context 之前,先试试传递 props 或者将 JSX 作为 children 传递。

对比 useState 和 useReducer

1)代码体积: 通常,在使用 useState 时,一开始只需要编写少量代码。
    而 useReducer 必须提前编写 reducer 函数和需要调度的 actions。
    但是,当多个事件处理程序以相似的方式修改 state 时,useReducer 可以减少代码量。
(2)可读性: 当状态更新逻辑足够简单时,useState 的可读性还行。
    但是,一旦逻辑变得复杂起来,它们会使组件变得臃肿且难以阅读。
    在这种情况下,useReducer 允许你将状态更新逻辑与事件处理程序分离开来。
(3)可调试性: 当使用 useState 出现问题时, 你很难发现具体原因以及为什么。 
    而使用 useReducer 时, 你可以在 reducer 函数中通过打印日志的方式来观察每个状态的更新,以及为什么要更新(来自哪个 action)。 
    如果所有 action 都没问题,你就知道问题出在了 reducer 本身的逻辑中。然而,与使用 useState 相比,你必须单步执行更多的代码。
(4)可测试性: reducer 是一个不依赖于组件的纯函数。这就意味着你可以单独对它进行测试。
    一般来说,我们最好是在真实环境中测试组件,但对于复杂的状态更新逻辑,
    针对特定的初始状态和 action,断言 reducer 返回的特定状态会很有帮助。
(5)个人偏好: 并不是所有人都喜欢用 reducer,没关系,这是个人偏好问题。
    你可以随时在 useState 和 useReducer 之间切换,它们能做的事情是一样的!

构建 state 的原则

1)合并关联的 state。
    如果你总是同时更新两个或更多的 state 变量,请考虑将它们合并为一个单独的 state 变量。
(2)避免互相矛盾的 state。
    当 state 结构中存在多个相互矛盾或“不一致”的 state 时,你就可能为此会留下隐患。应尽量避免这种情况。
(3)避免冗余的 state。
    如果你能在渲染期间从组件的 props 或其现有的 state 变量中计算出一些信息,则不应将这些信息放入该组件的 state 中。
(4)避免重复的 state。
    当同一数据在多个 state 变量之间或在多个嵌套对象中重复时,这会很难保持它们同步。应尽可能减少重复。
(5)避免深度嵌套的 state。
    深度分层的 state 更新起来不是很方便。如果可能的话,最好以扁平化方式构建 state。

slice 和 splice

slice 让你可以拷贝数组或是数组的一部分。
splice 会直接修改 原始数组(插入或者删除元素)。
在 React 中,更多情况下你会使用 slice(没有 p !),因为你不想改变 state 中的对象或数组。

为什么在 React 中不推荐直接修改 state

有以下几个原因:
调试:如果你使用 console.log 并且不直接修改 state,你之前日志中的 state 的值就不会被新的 state 变化所影响。
    这样你就可以清楚地看到两次渲染之间 state 的值发生了什么变化
优化:React 常见的优化策略依赖于如果之前的 props 或者 state 的值和下一次相同就跳过渲染。
    如果你从未直接修改 state ,那么你就可以很快看到 state 是否发生了变化。
    如果 prevObj === obj,那么你就可以肯定这个对象内部并没有发生改变。
新功能:我们正在构建的 React 的新功能依赖于 state 被像快照一样看待的理念。
    如果你直接修改 state 的历史版本,可能会影响你使用这些新功能。 
需求变更:有些应用功能在不出现任何修改的情况下会更容易实现,比如实现撤销/恢复、展示修改历史,
    或是允许用户把表单重置成某个之前的值。这是因为你可以把 state 之前的拷贝保存到内存中,并适时对其进行再次使用。
    如果一开始就用了直接修改 state 的方式,那么后面要实现这样的功能就会变得非常困难。      
更简单的实现:React 并不依赖于 mutation ,所以你不需要对对象进行任何特殊操作。
    它不需要像很多“响应式”的解决方案一样去劫持对象的属性、总是用代理把对象包裹起来,或者在初始化时做其他工作。
    这也是为什么 React 允许你把任何对象存放在 state 中——不管对象有多大——而不会造成有任何额外的性能或正确性问题的原因。     

在实践中,你经常可以“侥幸”直接修改 state 而不出现什么问题,
但是我们强烈建议你不要这样做,这样你就可以使用我们秉承着这种理念开发的 React 新功能。

state

要使用新数据更新组件,需要做两件事:
(1)保留渲染之间的数据。
(2)触发 React 使用新数据渲染组件(重新渲染)。

useState Hook 提供了这两个功能:
(1)State 变量用于保存渲染间的数据。
(2)State setter 函数更新变量并触发 React 再次渲染组件。

const [index, setIndex] = useState(0);
index 是一个 state 变量,setIndex 是对应的 setter 函数。
这里的 [] 语法称为数组解构,它允许你从数组中读取值。 useState 返回的数组总是正好有两项。

在 React 中,useState 以及任何其他以“use”开头的函数都被称为 Hook。
Hook 是特殊的函数,只在 React 渲染时有效。它们能让你 “hook” 到不同的 React 特性中去。

Hooks ——以 use 开头的函数——只能在组件或自定义 Hook 的最顶层调用。 
你不能在条件语句、循环语句或其他嵌套函数内调用 Hook。
Hook 是函数,但将它们视为关于组件需求的无条件声明会很有帮助。
在组件顶部 “use” React 特性,类似于在文件顶部“导入”模块。

State 是隔离且私有的
State 是屏幕上组件实例内部的状态。
换句话说,如果你渲染同一个组件两次,每个副本都会有完全隔离的 state!改变其中一个不会影响另一个。

摘要:
(1)当一个组件需要在多次渲染间“记住”某些信息时使用 state 变量。
(2)State 变量是通过调用 useState Hook 来声明的。
(3)Hook 是以 use 开头的特殊函数。它们能让你 “hook” 到像 state 这样的 React 特性中。
(4)Hook 可能会让你想起 import:它们需要在非条件语句中调用。调用 Hook 时,包括 useState,仅在组件或另一个 Hook 的顶层被调用才有效。
(5)useState Hook 返回一对值:当前 state 和更新它的函数。
(6)你可以拥有多个 state 变量。在内部,React 按顺序匹配它们。
(7)State 是组件私有的。如果你在两个地方渲染它,则每个副本都有独属于自己的 state。

更新 state 中的对象和数组,见官网: https://react.docschina.org/learn/updating-objects-in-state

响应事件

传递给事件处理函数的函数应直接传递,而非调用。例如:
传递一个函数(正确): <button onClick={
   handleClick}>
调用一个函数(错误): <button onClick={
   handleClick()}>
在第一个示例中,handleClick 函数作为 onClick 事件处理函数传递。这会让 React 记住它,并且只在用户点击按钮时调用你的函数。
在第二个示例中,handleClick() 中最后的 () 会在 渲染 过程中 立即 触发函数,即使没有任何点击。
    这是因为在 JSX {
   } 之间的 JavaScript 会立即执行。

当你编写内联代码时,同样的陷阱可能会以不同的方式出现:
传递一个函数(正确): <button onClick={
   () => alert('...')}>
调用一个函数(错误): <button onClick={
   alert('...')}>


阻止传播:
如果你想阻止一个事件到达父组件,你需要像下面 Button 组件那样调用 e.stopPropagation()function Button({
     onClick, children }) {
   
      return (
        <button onClick={
   e => {
   
          e.stopPropagation();
          onClick();
        }}>
          {
   children}
        </button>
      );
    }
    
    export default function Toolbar() {
   
      return (
        <div className="Toolbar" onClick={
   () => {
   
          alert('你点击了 toolbar !');
        }}>
          <Button onClick={
   () => alert('正在播放!')}>
            播放电影
          </Button>
          <Button onClick={
   () => alert('正在上传!')
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值