React hook 为什么useState不能再条件和循环中使用

  1. 首先弄清楚困扰我很久的一个问题 为什么useState不能再条件和循环中使用?

在查询了很多博客后最终在这篇博客中找到了答案

掘金

这篇文章从源码的角度向我们解释了这个why

首先当我们这样写时

  const [name,setName] = useState('杜皮')
  const [address,setAddress] = useState('杭州')
  
每一个useState都会在当前组件中创建一个hook对象  ,并且这个对象中的next属性始终执行下一个useState的hook对象
这些对象以一种类似链表的形式存在 Fiber.memoizedState 中
而函数组件就是通过fiber这个数据结构来实现每次render后name address不会被useState重新初始化

正是因为hooks中是这样存储state的 所以我们只能在hooks的根作用域中使用useState,而不能在条件语句和循环中使用
因为我们不能每次都保证条件或循环语句都会执行

if (something) {
  const [state1] = useState(1)
}

// or

for (something) {
  const [state2] = useState(2)
}
  1. fiber

每一个组件都会有一个fiber对象,在fiber中我们主要关注memoizedState这个对象,它就是调用完useState后对应的存储state的对象

调用useState后设置在memoizedState上的对象长这样:(又叫Hook对象)

{
  baseState,
  next,  
  baseUpdate,
  queue,
  memoizedState
}

这里面我们最需要关心的是memoizedState和next,memoizedState是用来记录这个useState应该返回的结果的,而next指向的是下一次useState对应的`Hook对象,即

hook1  ==>	Fiber.memoizedState
state1 === hook1.memoizedState
hook1.next	==>	hook2
state2	==>	hook2.memoizedState
....

原文链接: https://www.yuque.com/docs/share/2b86964c-d7f7-42fd-b02a-304f062fcd59?# 《React hook 原理》

### React Hooks 中 `useState` 的常见问题及解决方案 #### 1. 条件调用 `useState` 当在条件语句中调用 `useState` 或者其他任何 Hook 时,可能会遇到错误提示:“React Hook "useState" is called conditionally. React Hooks must be called in the exact same order in every component render”。这是因为 React 需要确保每次渲染时 Hook 被按相同顺序执行。 为了防止此类错误发生,在定义组件逻辑时应始终将所有的 Hook 放置在同一位置,并且不在循环、嵌套函数或条件判断内使用它们[^2]。 ```javascript // 错误示范:不要这样做 function MyComponent({ show }) { if (show) { const [state, setState] = useState(false); } } ``` 相反地,应该如下所示: ```javascript // 正确做法 function MyComponent({ show }) { const [state, setState] = useState(show ? false : null); useEffect(() => { if (!show && state !== null) { // 清理状态或其他操作... } else if (show && state === null) { // 初始化状态... } }, [show]); return ( <> {/* 组件内容 */} </> ); } ``` #### 2. 更新相同的状态值不触发重绘 对于基于函数式的更新方式来说,如果传递给 `setState()` 方法的新旧状态相等,则不会引起视图刷新。这与传统的类组件行为不同——即使新旧状态完全一致,类组件仍然会经历一次完整的生命周期并最终完成 DOM 更新过程[^3]。 因此,如果你的应用程序依赖于某些副作用来响应特定事件的发生(比如网络请求),那么可能需要注意这一点差异所带来的影响。可以通过比较前后两次的状态变化来进行额外处理。 ```jsx const [value, setValue] = useState(initialValue); useEffect(() => { // 当 value 发生实际变更时才执行此回调 }, [value]); setValue(newValue); // 如果 newValue 当前的 value 相同则不会触发重新渲染 ``` #### 3. 使用回调形式初始化 State 有时希望延迟计算初始状态直到首次渲染之前;为此可以传入一个返回期望初识值得工厂函数作为参数提供给 `useState` 。这种模式特别适用于那些需要访问 props 或其它外部资源才能决定其默认取值的情况。 ```javascript const initCount = () => Math.floor(Math.random() * 100); function Counter() { const [count, setCount] = useState(initCount()); return <p onClick={() => setCount(count + 1)}>Clicked {count} times</p>; } ``` #### 4. 缓解频繁创建事件处理器带来的性能开销 每当父级组件重新渲染时,默认情况下子组件中的所有事件处理器都会被重建。通过利用 `useCallback` 这样的高阶 hook 可以有效地减少不必要的重复工作量,从而提高整体应用效率[^4]。 ```javascript import React, { useState, useCallback } from 'react'; function ChildComponent({ incrementHandler }) { return <button onClick={incrementHandler}>Increment</button>; } export default function ParentComponent() { const [counter, setCounter] = useState(0); const handleIncrement = useCallback( () => setCounter(prevState => prevState + 1), [] ); return ( <div> <ChildComponent incrementHandler={handleIncrement} /> <span>{counter}</span> </div> ); } ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值