react系列-bind this

来源:互联网 发布:网络远程教育大学 编辑:程序博客网 时间:2024/06/08 23:21

react系列-bind this

问题

对于大多数前端开发来说,JavaScript 的 this 关键字会造成诸多困扰,由于 JavaScript 代码中的 this 指向并不清晰。在写react应用时,也会也到很多作用域绑定引起的问题,React组件ES6的写法,不会将方法内部的作用域自动绑定到组件的实例上。

下面展示一段问题代码

class Search extends Component {    static propTypes = {        onSearch: React.PropTypes.func.isRequired    }    onSearch() {        console.log('表单值:', this.field.getValues());        this.props.onSearch(this.field.getValues());    }    render(){        const {init} = this.field;        return <div>            <Form direction="hoz" labelAlign="left">                    <FormItem label="loginID:">                        <Input placeholder="请输入loginID" {...init('loginID')}/>                    </FormItem>                    <Button type="primary" onClick={this.onSearch}>搜索</Button>            </Form>        </div>    }}

如果你真的尝试这么做了, 你会发现在onSearch中,因为this指向的是全局对象window而报错。

解决办法

我们都知道常规改变函数作用域的无非3种(Fiontion.prototype.bind call apply 三兄弟),下面讲解一下在es6中bind作用域的几种方式。

  1. 使用Function.prototype.bind()

    class Search extends Component {    render(){        return <div>            <Form direction="hoz" labelAlign="left">                    <FormItem label="loginID:">                        <Input placeholder="请输入loginID" {...init('loginID')}/>                    </FormItem>                    <Button type="primary" onClick={this.onSearch.bind(this)}>搜索</Button>            </Form>        </div>    }}
  2. ES7函数绑定语法

    在 ES7 中,有一个关于 bind 语法 的提议,提议将 :: 作为一个新的绑定操作符, 而且已经收录在stage-0提案中,实际上::是Function.propotype.bind()的一种语法糖。 幸运的是,Babel已经提供了对这个新语法的支持。

    class Search extends Component {    render(){        return <div>            <Form direction="hoz" labelAlign="left">                    <FormItem label="loginID:">                        <Input placeholder="请输入loginID" {...init('loginID')}/>                    </FormItem>                    <Button type="primary" onClick={::this.onSearch}>搜索</Button>            </Form>        </div>    }}
  3. 在构造函数中bind this

    class Search extends Component {    constructor(props) {        super(props);        this.onSearch = this.onSearch.bind(this)    }    render(){        return <div>            <Form direction="hoz" labelAlign="left">                    <FormItem label="loginID:">                        <Input placeholder="请输入loginID" {...init('loginID')}/>                    </FormItem>                    <Button type="primary" onClick={this.onSearch}>搜索</Button>            </Form>        </div>    }}
  4. 使用箭头函数

    class Search extends Component {    render(){        return <div>            <Form direction="hoz" labelAlign="left">                    <FormItem label="loginID:">                        <Input placeholder="请输入loginID" {...init('loginID')}/>                    </FormItem>                    <Button type="primary" onClick={(...args)=>{                        this.onSearch( ...args)                    }}>搜索</Button>            </Form>        </div>    }}
  5. core-decorators.js

    core-decorators.js 为开发者提供了一些实用的 decorator,其中实现的autobind修饰器能使得方法中的this对象绑定到原始对象

class Search extends Component {    @autobind    onSearch() {        console.log('表单值:', this.field.getValues());        this.props.onSearch(this.field.getValues());    }    render(){        const {init} = this.field;        return <div>            <Form direction="hoz" labelAlign="left">                    <FormItem label="loginID:">                        <Input placeholder="请输入loginID" {...init('loginID')}/>                    </FormItem>                    <Button type="primary" onClick={this.onSearch}>搜索</Button>            </Form>        </div>    }}

总结

比较

这里我们讨论下以上几种将this绑定到react组件方案的缺点,优点自己体会吧。

方案1和方案2,缺点也很严重,这种方式破坏了组件的pure render,每次组件render时,子组件Button的onClick值都是重新赋值所得,会导致Button做一次无谓的render。而且函数绑定语法::属于es7草案中的特性,尚未纳入es标准。使用需要谨慎。

方案3和方案4会增加代码量

方案5需要引入第三方库,不过core-decorators.js提供了很多使用的装饰器。

场景

某些场景下,我们需要传递额外的参数,比如列表中删除操作,需要传id。常用的方案是方案1和方案4

// Function.prototype.bind()<Item onClick={this.doDelete.bind(this, id)}>删除</Item>// 箭头函数<Item onClick={(...args)=>{    this.doDelete(id, ...args)}}>删除</Item>
0 0