1.绑定构造函数
class A extends React.Component { constructor(props) { super(props) this._eventHandler = this._eventHandler.bind(this) } _eventHandler() { // ... } render() { return <div onClick={this._eventHandler} /> } }
2. render()中的箭头函数
class A extends React.Component { _eventHandler() { // ... } render() { return <div onClick={()=>{this._eventHandler()}} /> } }
3.绑定渲染()
class A extends React.Component { _eventHandler() { // ... } render() { return <div onClick={this._eventHandler.bind(this)} /> } }
4. ES2015类字段中的箭头功能
class A extends React.Component { _eventHandler = () => { // ... } render() { return <div onClick={this._eventHandler} /> } }
5. @autobind装饰
class A extends React.Component { @autobind _eventHandler() { // ... } render() { return <div onClick={this._eventHandler} /> } }
1是最安全的方式,因为它不需要babel进行构建时转换,但输入非常烦人.
由于在每个渲染和React diff算法上发生绑定,因此2和3具有性能影响
4和5要求打字少于1,但它们需要babel支持,可能还不是最终规格的一部分.除此之外,我非常反对注释的想法(来自Java后端背景我鄙视注释因为它们经常被过度使用而且过于神奇)
截至最新的Babel版本是4或5推荐和最安全(关于未来兼容性)绑定功能的方式?还有其他我不知道的方式吗?我应该继续使用1吗?此外,如果任何被认为可以安全使用的是有任何可以改变我的代码库使用它们的codemod吗?
编辑:@LucaFabbri指向reflective bind babel变换.它看起来很酷,但它需要一个我不喜欢的非标准babel插件,因为它不是非常安全的.我试图尽可能地避免构建时魔术,如果你在很长一段时间内只使用一个代码库就可以使用它们,但是如果你维护几个代码库,你需要每次都处理构建时魔法(加上没有弹出的create-react-app支持.
解决方法
你可能没有意识到的一种方法是我提出的一个方法(没有看到其他任何类似的东西)专门用于避免方法2和3在对某些数据数组执行.map时渲染组件列表需要在props上传递this.someInstanceMethod(withSomeArg).例如:
class CatList extends React.Component { static propTypes = { kitties: PropTypes.arrayOf(PropTypes.instanceOf(Cat)),} adoptKitty(cat) { this.setState({ loading: true }) return api.adopt(cat) .then(res => this.setState({ loading: false }) .catch(err => this.setState({ loading: false,err }) } render() { // ... other stuff ... {this.props.kitties.map(kitty => ( <PureKittyCat key={kitty.id} // ... the problem: onClick={() => this.adoptKitty(kitty)} /> ))} } }
目前还不清楚如何避免在.map中的props上传递函数文字,这不仅因为您需要绑定它,而且还需要将当前元素传递给实例方法.在这种情况下,大多数人只是放弃了将PureKittyCat变为React.PureComponent的想法.
我解决这个问题的方法是在Component实例上存储一个WeakMap来创建一个本地缓存(父组件的本地缓存),它将每个kitty对象与我想传递给相关PureKittyCat组件的任何方法相关联.它看起来像这样:
class CatList extends React.Component { static propTypes = { kitties: PropTypes.arrayOf(PropTypes.instanceOf(Cat)),} this.methodCache = new WeakMap() adoptKitty(cat) { this.setState({ loading: true }) return api.adopt(cat) .then(res => this.setState({ loading: false }) .catch(err => this.setState({ loading: false,err }) } render() { // ... other stuff... {this.props.kitties.map(kitty => { // ... the good stuff: if ( !this.methodCache.has(kitty) ) { this.methodCache.set(kitty,{ adopt: () => this.adoptKitty(kitty),// any other methods you might need }) } // as long is this is the same in-memory kitty,onClick will // receive the same in-memory function object every render return ( <PureKittyCat key={kitty.id} onClick={this.methodCache.get(kitty).adopt} /> ) })} } }
WeakMaps是avoid a memory-leak这样的正确选择.并且在组件上存储新缓存而不是作为全局缓存(在其自己的模块中),如果/当您尝试缓存同样的方法时,可以避免遇到名称冲突的可能性跨多个组件的同一对象(此处为Cats)的名称.
我在这里解释了这个问题的唯一解决办法:https://medium.freecodecamp.org/why-arrow-functions-and-bind-in-reacts-render-are-problematic-f1c08b060e36.
两者都涉及一些恼人的设置,但这确实是实现该场景的渲染优化的唯一方法.