Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

react系列-bind this #5

Open
amibug opened this issue Dec 22, 2017 · 0 comments
Open

react系列-bind this #5

amibug opened this issue Dec 22, 2017 · 0 comments

Comments

@amibug
Copy link
Owner

amibug commented Dec 22, 2017

问题


对于大多数前端开发来说,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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant