从 0 到 1 实现 react - 9.onChange 事件以及受控组件
2018-09-18 16:29
756 查看
该系列文章在实现 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的事件循环机制改变表单的值。
效果如下:
至此,模拟了受控组件的实现。
相关文章推荐
- 通过事件通知 实现react.js组件间通信
- react 实现双向绑定以及事件绑定
- React组件内事件传参实现tab切换的示例代码
- react 实现组件嵌套以及子组件与父组件之间的通信
- react-redux高阶组件connect方法使用介绍以及实现原理
- 基于Node的React图片上传组件实现
- Cocos2d-X 3.0 ControlButton的加入以及点击事件的实现
- React受控组件与非受控组件
- uiscrollview的touch事件 以及上面子视图touch事件 实现原理
- 利用接口实现自定义监听事件以及观察者模式
- React-Native的TextInput组件的设置以及获取输入框的内容
- react数据单向流、受控组件、子父组件通信
- android之实现各个组件点击事件处理
- android 组件内部实现触摸事件,更改背景
- Android项目类似淘宝 电商 搜索功能,监听软键盘搜索事件,延迟自动搜索,以及时间排序的搜索历史记录的实现
- 用 C# 做组件设计时的事件实现方法讨论
- 采用事件驱动编程以及GUI组件开发一个贷款计算器
- js阻止浏览器的默认行为以及停止事件冒泡(用JQuery实现回车提交)
- react实现筛选条件组件