React Mixin 双向绑定 及 源码分析

首先从没有使用mixin的例子引入

var BindingExample = React.createClass({
            getinitialState: function() {
                return {
                    text: ''
                }
            },handleChange: function(event) {
                this.setState({text: event.target.value})
            },render: function() {
                return <div>
                    <input type="text" placeholder="请输入内容" onChange={this.handleChange} />
                    <p>{this.state.text}</p>
                </div>
            }
        })
React.render(<BindingExample></BindingExample>,document.body);


这个例子大家都很熟悉,当input框发生改变的时候,去调用handleChange方法,然后通过setState重新render P标签中的数据


接下来引入使用mixin的一个例子,没有使用官方的方法

var BindingChange= {

        	handleChange: function(key) {

        		var that = this,newState = {};

        		return function(e) {

        			newState[key] = e.target.value;
        			that.setState(newState);

        		}

        	}

        }

        var BindingExample1 = React.createClass({

        	mixins: [BindingChange],getinitialState: function() {
        		return {
        			text: "",comment: ""
        		}
        	},render: function() {
        		return (
        			<div>
        			     <input type="text" placeholder="请输入内容" onChange={this.handleChange('text').bind(this)} />
	                             <textarea onChange={this.handleChange('comment').bind(this)}></textarea>
                                     <p>{this.state.text}</p>
                                     <p>{this.state.comment}</p>
        			</div>
        		);
        	}

        })
        React.render(<BindingExample1 />,document.body);

来分析一下这个例子

首先看BindingExample中的render,仔细观察一下实际上和例子一没有什么区别,只是在onChange处理的函数上绑定了上下文,还在在函数里面传了一个值(key)

这个key和state中对应的key值要相同,如例子中input改变的时候是修改state中的"text"这个状态,所以传入"text"。

然后再看BindingExample中的mixins,把BindingChange引用了进来,在引用之后,BindingChange中的东西就会变成BindingExample的东西

所以onChange那里进行bind之后,BindingChange的this指针指向了BindingExample,而不是window。


接着看一下BindingChange。先定义了一个that来保存当前的this,接下里定义了一个对象

为什么要定义一个对象呢。

可以发现,React中setState要修改的状态名字不是一个字符串,也不能解析变量。如果假如onChange事件中传入的key为"text",直接这样修改 setState({key:event.target.value}) 实际上并没有修改text的值,然是去修改了key的值,然而我们并没有在实际的代码中存在key这个状态,所以并不会发生改变。

所以在return的那个函数中newState[key] = e.target.value,这个时候key就可以解析成了text,并且进行了赋值,然后将该对象传入setState中,text这个状态得以改变。

当然,假如传入的是comment这个值,那么这个key实际上就是comment,然后改变的是comment的状态


读到这里大家可能都发现了,mixin做的就是把一些相同的东西抽离出来,就像这个onChange事件,如果没用mixin的话,可能就要写onChange1和onChange2。实际上就像方法的封装一下。当然也有不好的地方,就是查找这个方法的时候可能不会一下子找到。


最后引入官方的例子

       var BindingExample2 = React.createClass({
        	mixins: [React.addons.LinkedStateMixin],render: function() {
        		return (
        			<div>
        				<input type="text" placeholder="请输入内容" valueLink={this.linkState('text')} />
                                        <textarea valueLink={this.linkState('comment')}></textarea>
                                        <p>{this.state.text}</p>
                                        <p>{this.state.comment}</p>
        			</div>
        		);
        	}
        });
        React.render(<BindingExample2 />,document.body);
对比例子2,我们发现在mixin中使用了React.addons.LinkedStateMixin,这个就是官方封装好的自己的双向绑定的方法

再往下读下去发现onChange事件变成了valueLink,然后使用了方法linkState,而传入的值依旧是这个input想要改变的状态的名字。


我们来看一下官方的这个方法是怎么封装的

   var ReactLink = _dereq_(75);
        var ReactStateSetters = _dereq_(94);

        var LinkedStateMixin = {
            linkState: function(key) {
                return new ReactLink(
                    this.state[key],ReactStateSetters.createStateKeySetter(this,key)
                );
            }
        };

        module.exports = LinkedStateMixin;
这个就是方法封装的mixin的LinkedStateMixin方法

官方是这么解释的

ReactLink encapsulates a common pattern in which a component wants to modify a prop received from its parent.

ReactLink封装了一个需要从父级获取值并且修改一个公有的模式。(实际上就是绑定上下文环境,然后函数的封装)


然后我们去查看ReactStateSetters.createStateKeySetter这个方法是这样写的

  createStateKeySetter: function(component,key) {
    // Memoize the setters.
    var cache = component.__keySetters || (component.__keySetters = {});
    return cache[key] || (cache[key] = createStateKeySetter(component,key));
  }
传入上下文环境,传入要修改的状态的名字。看到cache,大家也能明白这是缓存的意思,判断是否有这个状态,有的话直接复制,并返回。没有的话就创建一个对象,然后进入(cache[key] = createStateKeySetter(component,key))这个方法


然后继续找到了createStateKeySetter,他是这个样子的

function createStateKeySetter(component,key) {
  var partialState = {};
  return function stateKeySetter(value) {
    partialState[key] = value;
    component.setState(partialState);
  };
}
继续传入上下文环境,传入要修改的状态的名字。然后往下读,是不是突然发现很熟悉,这个不就是和例子二中的handleChange是一模一样的。


此篇博文到此就结束了。

相关文章

react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接...
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc ...