从 0 到 1 实现 react - 9.onChange 事件以及受控组件

该系列文章在实现 cpreact 的同时理顺 React 框架的核心内容

项目地址

从一个疑问点开始

接上一章 HOC 探索 抛出的问题 ———— react 中的 onChange 事件和原生 DOM 事件中的 onchange 表现不一致,举例说明如下:

// React 中的 onChange 事件
class App extends Component {
  constructor(props) {
    super(props)
    this.onChange = this.onChange.bind(this)
  }

  onChange(e) {
    console.log('键盘松开立刻执行')
  }

  render() {
    return (
      <input onChange={this.onChange} />
    )
  }
}

/*--------------分割线---------------*/

// 原生 DOM 事件中的 onchange 事件:<input id='test'>
document.getElementById('test').addEventListener('change', (e) => {
  console.log('键盘松开以后还需按下回车键或者点下鼠标才会触发')
})

拨云见雾

我们来看下 React 的一个 issue React Fire: Modernizing React DOM。有两点信息和这篇文章的话题相关。

  • Drastically simplify the event system
  • Migrate from onChange to onInput and don’t polyfill it for uncontrolled components

从这两点内容我们可以得知下面的信息:

React 实现了一套合成事件机制,也就是它的事件机制和原生事件间会有不同。比如它目前 onChange 事件其实对应着原生事件中的 input 事件。在这个 issue 中明确了未来会使用 onInput 事件替代 onChange 事件,并且会大幅度地简化合成事件。

有了以上信息后,我们对 onChange 事件(将来的 onInput 事件)的代码作如下更改:

function setAttribute(dom, attr, value) {
  ...
  if (attr.match(/on\w+/)) {        // 处理事件的属性:
    let eventName = attr.toLowerCase().substr(2)
    if (eventName === 'change') { eventName = 'input' } // 和现阶段的 react 统一
    dom.addEventListener(eventName, value)
  }
  ...
}

自由组件以及受控组件

区分自由组件以及受控组件在于表单的值是否由 value 这个属性控制,比较如下代码:

const case1 = () => <input />                    // 此时输入框内可以随意增减任意值
const case2 = () => <input defaultValue={123} /> // 此时输入框内显示 123,能随意增减值
const case3 = () => <input value={123} />        // 此时输入框内显示 123,并且不能随意增减值

case3 的情形即为简化版的受控组件。

受控组件的实现

题目可以换个问法:当 input 的传入属性为 value 时(且没有 onChange 属性),如何禁用用户的输入事件的同时又能获取焦点?

首先想到了 html 自带属性 readonly、disable,它们都能禁止用户的输入,但是它们不能满足获取焦点这个条件。结合前文 onChange 的实现是监听 input 事件,代码分为以下两种情况:

1.dom 节点包含 value 属性、onChange 属性
2.dom 节点包含 value 属性,不包含 onChange 属性

代码如下:

function vdomToDom(vdom) {
  ...
  if (vdom.attributes
    && vdom.attributes.hasOwnProperty('onChange')
    && vdom.attributes.hasOwnProperty('value')) { // 受控组件逻辑
      ...
      dom.addEventListener('input', (e) => {
        changeCb.call(this, e)
        dom.value = oldValue
      })
      ...
    }
  if (vdom.attributes
    && !vdom.attributes.hasOwnProperty('onChange')
    && vdom.attributes.hasOwnProperty('value')) { // 受控组件逻辑
    ...
    dom.addEventListener('input', (e) => {
      dom.value = oldValue
    })
    ...
  }
  ...
}

可以发现它们的核心都在这段代码上:

dom.addEventListener('input', (e) => {
  changeCb.call(this, e)
  dom.value = oldValue
})

区别是当有 onChange 属性 时,能提供相应的回调函数 changeCb 通过事件循环机制改变表单的值。看如下两个例子的比较:

const App = () => <input value={123} />

效果如下:

class App extends Component {
  constructor() {
    super()
    this.state = { num: 123 }
    this.change = this.change.bind(this)
  }

  change(e) {
    this.setState({
      num: e.target.value
    })
  }

  render() {
    return (
      <div>
        <input value={this.state.num} onChange={this.change} />
      </div>
    )
  }
}

这段代码中的 change 函数即上个段落所谓的 changeCb 函数,通过 setState 的事件循环机制改变表单的值。

效果如下:

至此,模拟了受控组件的实现。

原文地址:https://www.cnblogs.com/MuYunyun/p/9669679.html

时间: 2024-10-13 07:51:54

从 0 到 1 实现 react - 9.onChange 事件以及受控组件的相关文章

react使用pubsub事件订阅,组件间通信

1.PubSub使用方式 1.1 react导入库 npm install pubsub-js --save 1.2 react 页面引入pubsubjs import PubSub from 'pubsub-js' 1.3 pubsubjs使用 发送消息:PubSub.publish(名称,参数) 订阅消息:PubSub.subscrib(名称,函数) 取消订阅:PubSub.unsubscrib(名称) PS:pubsubjs源码及使用详情https://github.com/mroderi

浅谈react受控组件与非受控组件

最近在使用蚂蚁金服出品的一条基于react的ant-design UI组件时遇到一个问题,编辑页面时input输入框会展示保存前的数据,但是是用defaultValue就是不起作用,输入框始终为空值而不是具体的传入的值.具体编辑页面中文本框相关的代码如下:         ... //render方法上面的内容省略  <FormItem       label="问题描述:"       hasFeedback      {...props.formItemLayout}  &g

从 0 到 1 实现 React 系列 —— 5.PureComponent 实现 &amp;&amp; HOC 探幽

本系列文章在实现一个 cpreact 的同时帮助大家理顺 React 框架的核心内容(JSX/虚拟DOM/组件/生命周期/diff算法/setState/PureComponent/HOC/...) 项目地址 从 0 到 1 实现 React 系列 -- JSX 和 Virtual DOM 从 0 到 1 实现 React 系列 -- 组件和 state|props 从 0 到 1 实现 React 系列 -- 生命周期和 diff 算法 从 0 到 1 实现 React 系列 -- 优化 se

从 0 到 1 实现 React 系列 —— 4.setState优化和ref的实现

看源码一个痛处是会陷进理不顺主干的困局中,本系列文章在实现一个 (x)react 的同时理顺 React 框架的主干内容(JSX/虚拟DOM/组件/生命周期/diff算法/setState/ref/...) 从 0 到 1 实现 React 系列 -- JSX 和 Virtual DOM 从 0 到 1 实现 React 系列 -- 组件和 state|props 从 0 到 1 实现 React 系列 -- 生命周期和 diff 算法 从 0 到 1 实现 React 系列 -- 优化 set

React中,input外边如果包一个div,可以把input的onChange事件绑定到div上面,并且也生效

最近第一次开始学习封装组件,遇到几个比较神奇的问题. 首先就是如果input外边包一个div,如果把input的onChange事件绑定到div上,也会生效 <div onChange={(e)=>{console.log(e.target.value)}}> <Input/> </div> 还有,如果封装了一个组件,那么里面的最外层元素的{...this.props}需要慎用,虽然这样子做,可以把样式传过来,并且生效,但是会报错 原文地址:https://www

关于下拉框的onchange事件和onclick选择value值。

下拉框的onchange事件和onclick,一般最好都选择onchange事件,onclick可能会不兼容有些浏览器. 下面是代码: <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>选择</title> <script src="js/lib/jquery/v1.12.3/jquery-1.

jquery触发两次onchange事件

在项目中需要给select绑定改变事件,来触发动作,但是发现改变一次select,onchange方法执行两遍 //公告类别的变更时间联动公告模板$("#test").change(function () { alert(1);}) 后来给select 标签添加一个onchange属性也会触发两次,这个onchange时间的机制是,在获取焦点之后,储存一个select里面的值,当发现这个值改变的时候,触发onchange事件 目前仅在IE8下发现此类问题,我仅测试了IE8/Chrome

onmouseout事件与onchange事件分析

今天修改Bug,新到期时间为:原到期时间+续卡时间 而不是 当前日期+续卡时间.在修改的过程中发现一个Bug.那就是续卡时间数值框我输入新数值A之后,点击窗体空白处,新到期时间并没有修改过来, 再次点击数值输入框,输入新的数值B后,新到期时间才更改过来,并且为之前的A+原到期时间. 检查代码:续卡时间框为onmouseout事件才去检查数值修改新到期时间: 1 <td align="left"> 2 <input type="text" id=&q

select的onChange事件问题解决

一.onChange事件只有在值改变时才可触发,所以必须在每一次选择时(尤其第一次)保证选择的值是改变的! 所以<select name="inv_payee" id="ECS_INVPAYEE" onchange="show_payee();" style="border:1px solid #ccc;">    <option value="个人" selected >个人<