React 新版官方文档 (二) useState 用法详解

在这里插入图片描述

背景

本文默认读者对 useState 有最为基本的了解,比如知道他的写法应当是怎样的,下面着重介绍部分重要的、在开发过程中会踩的坑和一些特性,最后动手实现一个最基本的 useState 代码

useState

⭐️ 注意事项:

  1. 状态只在下次更新时异步变化,如果在设置状态后立即读取值还是老状态的

  2. 如果提供的新状态与当前状态相同 (使用 Object.is 比较),将跳过重新渲染组件以及子组件

  3. React 是批量合并更新 state 的。多个状态更新操作会被放入一个队列中,然后在适当的时机进行合并和批量处理。这可以防止在单个事件期间多次重新渲染,一定会按照顺序。在极少数情况下,需要强制 React 提前更新界面,例如访问 DOM,可以使用 flushSync。
    🌰 例子:

  4. 设置状态后立即读取状态

  5. 使用 setState 并不会改变已执行代码的当前状态 只会影响从下一次渲染开始返回的内容,想获取到变化后的值,可以选用 useEffect
    function handleClick() {
    setName(‘Robin’);
    console.log(name); // Still “Taylor”!
    }

  6. 基于上一次状态更新值
    注意 setState 特性
    function handleClick() {
    // ❌
    setAge(age + 1); // setAge(42 + 1)
    setAge(age + 1); // setAge(42 + 1)
    setAge(age + 1); // setAge(42 + 1)

    // ✅
    setAge(a => a + 1); // setAge(42 => 43)
    setAge(a => a + 1); // setAge(43 => 44)
    setAge(a => a + 1); // setAge(44 => 45)
    }

  7. 更新状态中的对象和数组
    应该替换它而不是修改它
    React 中状态应该是只读的
    // 🚩 Don’t mutate an object in state like this:
    form.firstName = ‘Taylor’;
    // ✅ Replace state with a new object
    setForm({
    …form,
    firstName: ‘Taylor’
    });

  8. 避免重新创建初始状态
    createInitialTodos 有效执行只有组件挂载时一次,但在之后组件历次re-render时都会被执行,这就造成了资源浪费 只需要传递初始化函数声明就行
    function TodoList() { // ❌
    const [todos, setTodos] = useState(createInitialTodos());
    // …
    function TodoList() { // ✅
    const [todos, setTodos] = useState(createInitialTodos);
    // …
    其他问题:https://react.dev/reference/react/useState#troubleshooting

动手实现一个 useState

// 存储状态的数组
let state = []
// 存储更改状态方法的数组
let setters = []
// 用来记录状态和更改状态方法对应关系的下标
let stateIndex = 0

function createSetter(index) {
  return function (newState) {
    state[index] = newState
    render()
  }
}

function useState(initialState) {
  state[stateIndex] = state[stateIndex] ? state[stateIndex] : initialState
  // 采用闭包缓存每个state对应的setState
  setters.push(createSetter(stateIndex))
  const value = state[stateIndex]
  const setter = setters[stateIndex]
  // 每创建完一组都要+1,用来作为下一组状态的索引
  stateIndex++
  return [value, setter]
}

// 因为状态更改要刷新视图,因此这里用ReactDom.render方法来模拟更改状态后刷新视图的操作
function render() {
  // 每次调用render都要重置stateIndex,否则对应的索引无限递增将无法正确匹配state和setState之间的关系
  stateIndex = 0
  ReactDom.render(<App />, document.getElementById('root'))
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zoe_ya

如果你成功申请,可以打赏杯奶茶

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值