react组件放在数组中_React源码中如何实现受控组件

本文深入探讨了React中受控组件的工作原理,揭示了React如何通过非受控形式处理输入元素的value属性,以避免光标位置丢失的问题。在React的更新流程中,对于input等表单元素,React采取了一条特殊的discreteUpdate路径,确保在不破坏用户体验的同时实现受控状态。文章还通过实例分析了正常受控组件和未更新状态的受控组件之间的区别,并强调了实现一个前端框架需要关注的细节。
摘要由CSDN通过智能技术生成

今天我们站在框架开发者的角度来聊聊如何实现受控组件

React中一个简单的受控组件如下:

function App() {
  const [num, updateNum] = React.useState(0);
  
  const onChange = ({target: {value}}) => {
    updateNum(value);
  }

  return (
    <input value={num} onChange={onChange}/>
  )
}

onChange中会更新numnum作为value prop传递给,达到value受控的目的。

如果让你来设计,你会怎么做?

我相信大部分同学第一个想法是:将value prop与其他attribute prop一样处理就行。

我们知道React内部运行有3个阶段:

  • schedule 调度更新阶段

  • render 进行diff算法的阶段

  • commit 进行DOM操作的阶段

假设我们要在onChange中触发更新改变className,只需要在render阶段记录要改变的className,在commit阶段执行对应的addClass DOM操作。

同样的,如果我们要在onChange中触发更新改变value,只需要在render阶段记录要改变的value,在commit阶段执行对应的inputDOM.setAttribute('value', value)操作。

这样逻辑非常通顺。那么事实上呢?

直接改变value的问题

className只是inputDOM上的一个普通属性。而value则涉及到输入框光标的位置。

如果我们直接修改value,那么属性改变后input的光标输入位置也会丢失,光标会跳到输入框的最后。

想想我们将1234修改为12534

1234 --> 12534

需要先将光标位置移动到2之后,再输入5。

如果setAttribute('value', '12534'),那么光标不会保持在5后面而是跳到4后面。

那么React如何解决这个问题呢?

用非受控的形式实现受控组件

你没有看错,React非受控形式实现了受控组件的逻辑。

简单的说,不同于classNamecommit阶段受控更新,value则完全是非受控的形式,只在必要的时候受控更新。

因为一旦更新value,那么光标位置就会丢失。

我们稍微修改下Demo,input为受控组件,value始终为1:

function App() {
  const num = 1;

  return (
    <input value={num}/>
  )
}

当我们在源码中打上断点,输入2后,实际上会先显示12,再删掉2。

只不过这个删除的过程是同步的所以看起来输入框内始终只有1。

5b86c0a7927c0ec563f5d2886fb179d0.png

所以,不同于React其他组件props的更新会经历schedule - render - commit流程。

对于inputtextareaselectReact有一条单独的更新路径,这条路径触发的更新被称为discreteUpdate

这条路径的工作流程如下:

  1. 先以非受控的形式更新表单DOM

  2. 同步的优先级开启一次更新

  3. 更新后的valuecommit阶段并不会像其他props一样作用于DOM

  4. 调用restoreStateOfTarget方法,比较DOM的实际value(即步骤1中的非受控value)与步骤3中更新的value,如果相同则退出,如果不同则用步骤3的value更新DOM

什么情况下这2个value会相同呢?

我们正常的受控组件就是相同的情况:

function App() {
  const [num, updateNum] = React.useState(0);
  
  const onChange = ({target: {value}}) => {
    updateNum(value);
  }

  return (
    <input value={num} onChange={onChange}/>
  )
}

什么情况下这2个value会不同呢?

上面的Demo中,虽然受控,但是没有调用updateNum更新value的情况:

function App() {
  const num = 1;

  return (
    <input value={num}/>
  )
}

在这种情况下,步骤1的非受控value变为了12,步骤3的受控value还是1,所以最终会用1再更新下DOM的value

总结

可以看到,要实现一个完备的前端框架,是有非常多细节的。

为了实现受控组件,就得脱离整体更新流程,单独实现一套流程。

更多React源码细节,欢迎关注我的公众号魔术师卡颂

914a904db9780bb86e26ccf3916b9311.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值