从 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 的事件循环机制改变表单的值。
效果如下:

至此,模拟了受控组件的实现。
从 0 到 1 实现 react - 9.onChange 事件以及受控组件的更多相关文章
- react radio onchange事件点击无效
记: 项目需求: 页面中radio默认选中 第一次进去页面 点击radio的时候不管怎样点击 都是选中 连onChange事件都没触发 进入页面 点击刷新 点击rad ...
- 学习React系列(四)——受控组件与非受控组件
受控组件:通过组件的状态与属性的改变来控制组件 不可控组件:直接通过底层的dom来控制组件(具体来说就是通过绑定再底层dom上的方法来实现的,比如说ref,onChange) 受控组件 functio ...
- React受控组件和非受控组件
受控组件和非受控组件主要是用来解决表单组件状态谁来控制的问题.因为用户的输入会反应在界面上,相当于视图的状态发生了变化,而react是通过虚拟DOM比对修改视图的,这里就要决定谁来控制表单组件的状态. ...
- React:受控组件与非受控组件混用实战 - 译文
原文链接:React: hybrid controlled components in action 受控组件 非受控组件 混用受控组件和非受控组件 原则一 原则二 原则三 原则四 实施方案 总结 F ...
- 浅谈react受控组件与非受控组件
引言 最近在使用蚂蚁金服出品的一条基于react的ant-design UI组件时遇到一个问题,编辑页面时input输入框会展示保存前的数据,但是是用defaultValue就是不起作用,输入框始终为 ...
- 从 0 到 1 实现 React 系列 —— 5.PureComponent 实现 && HOC 探幽
本系列文章在实现一个 cpreact 的同时帮助大家理顺 React 框架的核心内容(JSX/虚拟DOM/组件/生命周期/diff算法/setState/PureComponent/HOC/...) ...
- 前端必读2.0:如何在React 中使用SpreadJS导入和导出 Excel 文件
最近我们公司接到一个客户的需求,要求为正在开发的项目加个功能.项目的前端使用的是React,客户想添加具备Excel 导入/导出功能的电子表格模块. 经过几个小时的原型构建后,技术团队确认所有客户需求 ...
- 从 0 到 1 实现 React 系列 —— 1.JSX 和 Virtual DOM
看源码一个痛处是会陷进理不顺主干的困局中,本系列文章在实现一个 (x)react 的同时理顺 React 框架的主干内容(JSX/虚拟DOM/组件/生命周期/diff算法/setState/ref/. ...
- 从 0 到 1 实现 React 系列 —— 4.setState优化和ref的实现
看源码一个痛处是会陷进理不顺主干的困局中,本系列文章在实现一个 (x)react 的同时理顺 React 框架的主干内容(JSX/虚拟DOM/组件/生命周期/diff算法/setState/ref/. ...
随机推荐
- (python)面向对象
一.面向对象概述 要了解面向对象,就需要先了解面向过程的概念,那么什么是面向过程编程呢?最具代表性的就是C语言了,所谓面向过程编程就是在做一件事的时候,需要按步骤进行,第一步干什么,第二步干什么,这种 ...
- PowerDesigner 12.5 汉化包-CSDN下载
来源 csdn积分下载的. 人们太小家子气,随随便便文件要那么多积分. 地址 链接: https://pan.baidu.com/s/1cwc24Y 密码: cr9k
- Response()的对象
addCookie(Cookie cookie):这个方法是向Response容器中添加一个Cookie,然后服务器容器会自动的将这个Cookie回写给客户机的,至于Cookie的相关知识我们会在后面 ...
- shell编程-函数(九)
每种语言都有自己的函数,shell也不例外.支持函数,它可以将脚本程序划分成一个个相对独立的代码块,使代码的模块化,结构更加清晰,并有效地减少程序的代码量,提高代码的复用率. 函数格式 functio ...
- 爬取mzi.com妹子图片网站(requests库)
看了崔大佬的文章,写了这个爬虫,学习了!原文地址 现在该网站加了反爬机制,不过在headers里加上refere参数就行了. 以下代码仅做学习记录之用: from bs4 import Beautif ...
- Linux CFS调度器之pick_next_task_fair选择下一个被调度的进程--Linux进程的管理与调度(二十八)
1. CFS如何选择最合适的进程 每个调度器类sched_class都必须提供一个pick_next_task函数用以在就绪队列中选择一个最优的进程来等待调度, 而我们的CFS调度器类中, 选择下一个 ...
- c/c++二叉树的创建与遍历(非递归遍历左右中,破坏树结构)
二叉树的创建与遍历(非递归遍历左右中,破坏树结构) 创建 二叉树的递归3种遍历方式: 1,先中心,再左树,再右树 2,先左树,再中心,再右树 3,先左树,再右树,再中心 二叉树的非递归4种遍历方式: ...
- Hibernate 5 入门指南-基于映射文件
由于Hibernate 4版本混乱,Hibernate 3有些过时,Hibernate 5的开发文档尚不完善,所以构建一份简单的Hibernate 5的入门指南 注:案例参考Hibernate 官方参 ...
- 数据执行保护呈灰色无法开启 用命令BCEDIT无效 请问怎么解决?
1.默认的管理员账号是关闭开不了的. 2.你重新创建个管理员账户. 3.然后登陆这个账号. 4.然后打开cmd 输入 bcdedit /set nx optin alwaysoff 这行指令.然后回车 ...
- (转载)关于usr/bin/ld: cannot find -lxxx问题总结
usr/bin/ld: cannot find -lxxx问题总结 linux下编译应用程序常常会出现如下错误: /usr/bin/ld: cannot find -lxxx 意思是 ...