我打破了 React Hook 必须按顺序、不能在条件语句中调用的枷锁

React 官网介绍了 Hook 的这样一个限制:

不要在循环,条件或嵌套函数中调用 Hook, 确保总是在你的 React 函数的最顶层以及任何 return 之前调用他们。遵守这条规则,你就能确保 Hook 在每一次渲染中都按照同样的顺序被调用。这让 React 能够在多次的 useStateuseEffect 调用之间保持 hook 状态的正确。(如果你对此感到好奇,我们在下面会有更深入的解释。)

这个限制在开发中也确实会时常影响到我们的开发体验,比如函数组件中出现 if 语句提前 return 了,后面又出现 Hook 调用的话,React 官方推的 eslint 规则也会给出警告。

function App(){
  if (xxx) {
    return null;
  }

  // ❌ React Hook "useState" is called conditionally. 
  // React Hooks must be called in the exact same order in every component render.
  useState();

  return 'Hello'
}
复制代码

其实是个挺常见的用法,很多时候满足某个条件了我们就不希望组件继续渲染下去。但由于这个限制的存在,我们只能把所有 Hook 调用提升到函数的顶部,增加额外开销。

由于 React 的源码太复杂,接下来本文会以原理类似但精简很多的 Preact 的源码为切入点来调试、讲解。

限制的原因

这个限制并不是 React 团队凭空造出来的,的确是由于 React Hook 的实现设计而不得已为之。

以 Preact 的 Hook 的实现为例,它用数组和下标来实现 Hook 的查找(React 使用链表,但是原理类似)。

// 当前正在运行的组件
let currentComponent

// 当前 hook 的全局索引
let currentIndex

// 第一次调用 currentIndex 为 0
useState('first') 

// 第二次调用 currentIndex 为 1
useState('second')
复制代码

可以看出,每次 Hook 的调用都对应一个全局的 index 索引,通过这个索引去当前运行组件 currentComponent 上的 _hooks 数组中查找保存的值,也就是 Hook 返回的 [state, useState]

那么假如条件调用的话,比如第一个 useState 只有 0.5 的概率被调用:

// 当前正在运行的组件
let currentComponent

// 当前 hook 的全局索引
let currentIndex

// 第一次调用 currentIndex 为 0
if (Math.random() > 0.5) {
  useState('first')
}

// 第二次调用 currentIndex 为 1
useState('second')
复制代码

在 Preact 第一次渲染组件的时候,假设 Math.random() 返回的随机值是 0.6,那么第一个 Hook 会被执行,此时组件上保存的 _hooks

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: React HookReact 函数组件的一种特殊函数,它们的调用必须遵循一定规则,否则会导致应用程序出现错误。 其一个规则是,React Hook 只能在 React 函数组件的顶层作用域调用不能在循环、条件或嵌套函数调用。这是因为 React Hook 实现时依赖于 React 内部的 Fiber 树结构,如果在循环、条件或嵌套函数调用 React Hook,就会打破 Fiber 树结构的规则,导致 React Hook 的状态无法正确更新,从而出现错误。 另外,React Hook 调用不能出现在任何非 React 的函数,因为它们需要在 React 组件的生命周期正确执行。 因此,为了避免出现错误,应该在 React 函数组件的顶层作用域调用 React Hook。 ### 回答2: React Hook 是一种用于在函数组件处理状态和副作用的特殊函数。根据 React 的规则,我们不能在循环、条件或嵌套函数直接调用 React Hook。 这主要是因为 React 需要根据每次渲染之间的顺序和数量来保持状态的一致性。在循环使用 Hook 可能导致组件内部状态错误地共享,从而导致不一致的渲染结果。 在条件语句调用 Hook 也是不允许的,因为条件语句只在组件的渲染过程执行一次,并不会在每次渲染时都执行。如果我们在条件语句使用 Hook,那么 React 无法保证状态的正确更新和一致性。 另外,由于闭包的存在,嵌套函数在每次渲染时都会创建新的函数实例。如果我们在嵌套函数使用 Hook,那么每个函数返回的状态和副作用都将是独立的,无法建立正确的连接和共享状态。 为了解决这些问题,ReactHook 的使用进行了限制。它确保我们在组件的每次渲染周期都以相同的顺序调用 Hook,并通过使用特殊的内部索引来跟踪 Hook 的状态。 如果我们需要在循环、条件或嵌套函数使用 Hook,可以使用其他方法来达到相同的效果,比如使用数组来存储状态,或使用 useRef 来保存结果。这样可以绕过 ReactHook 的限制,并保持状态的一致性和正确更新。 ### 回答3: 不能在循环、条件或嵌套函数调用 React Hook 是因为 React Hook 的使用规则要求在每次渲染时按照相同的顺序调用 Hook,以确保 hook调用顺序在每次渲染时保持一致。同时,这也是为了确保 React 在组件更新时能正确地跟踪和管理它们。 如果在循环调用 Hook,可能会导致 hook调用顺序出现变化,进而导致组件状态管理出现错误。循环可能会多次执行,而每次循环执行时,hook调用顺序和次数都可能不一样,这将影响到组件内部的状态和副作用管理。 同样的道理也适用于在条件语句调用 Hook。由于条件语句的结果可能在组件的不同渲染周期发生变化,如果在条件语句调用 Hook,可能会导致 hook调用顺序不一致,进而引起组件状态的混乱。 在嵌套函数调用 Hook 也会遇到类似的问题。由于嵌套函数的调用位置可能会发生变化,如果在嵌套函数调用 Hook,就无法保证 hook调用顺序在组件的每次渲染保持一致,从而可能导致状态管理出现错误。 因此,为了遵循 React Hook 的使用规则,保持 hook调用顺序一致,我们应该在函数组件的最顶层作用域调用 Hook,避免在循环、条件或嵌套函数调用 Hook,以确保组件状态的正确管理和渲染

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值