为什么在 React 的 Render 中使用箭头函数和 bind 会造成问题
2018-01-10 16:26
375 查看
原文转自https://zhuanlan.zhihu.com/p/29266705
在 render 中使用箭头函数或绑定会导致子组件重新渲染,即使 state 并没有改变。作者推荐使用提取子组件或在 HTML 元素中传递数据的方式来避免绑定。
这个例子中,我在 render 中使用一个箭头函数来绑定每个删除按钮对应的用户 ID。
点击 CodeSandbox 来查看及演示完整的 demo。(CodeSandbox 很酷,是一个 React 在线编辑器,可以实时编译和展示当前的界面。)
import React from 'react'; import { render } from 'react-dom'; import User from './User'; class App extends React.Component { constructor(props) { super(props); this.state = { users: [ { id: 1, name: 'Cory' }, { id: 2, name: 'Meg' }, { id: 3, name: 'Bob' } ] }; } deleteUser = id => { this.setState(prevState => { return { users: prevState.users.filter( user => user.id !== id) } }) } render() { return ( <div> <h1>Users</h1> <ul> { this.state.users.map( user => { return <User key={user.id} name={user.name} onDeleteClick={() => this.deleteUser(user.id)} /> }) } </ul> </div> ); } } export default App; render(<App />, document.getElementById('root'));
在 35 行中,我使用了一个箭头函数将一个值传递给了 deleteUser 函数,这就是问题的所在。
要查看为什么会这样,先来看看 User.js:
import React from 'react'; // Note how the debugger below gets hit when *any* delete // button is clicked. Why? Because the parent component // uses an arrow function, which means this component // class User extends React.PureComponent { render() { const {name, onDeleteClick } = this.props console.log(`${name} just rendered`); return ( <li> <input type="button" value="Delete" onClick={onDeleteClick} /> {name} </li> ); } } export default User;
每次 render 调用时,控制台上都会打印日志。User 已经被声明为 PureComponent。所以 User 应该只在 props 或者 state 改变时才会重新 render。但是,当你点击 delete 按钮时,对于每一个 User 实例,都会调用 render。
原因在于:父组件在 props 中传递了一个箭头函数。箭头函数在每次 render 时都会重新分配(和使用 bind 的方式相同)。所以,尽管我将 User 声明为 PureComponent,User 的父组件中的箭头函数导致 User 组件为所有的用户实例传递了一个新的函数。所以当点击任何删除按钮时,每个用户实例都会重新 render。
结论:
避免在 render 中使用箭头函数和绑定。否则会打破 shouldComponentUpdate 和 PureComponent 的性能优化。
应该怎么做?
先来看个例子比较一下在 render 中不使用箭头函数的差异。点击 CodeSandbox 查看和运行完整 demo。
import React from 'react'; import { render } from 'react-dom'; import User from './User'; class App extends React.Component { constructor(props) { super(props); this.state = { users: [ { id: 1, name: 'Cory' }, { id: 2, name: 'Meg' }, { id: 3, name: 'Bob'} ], }; } deleteUser = id => { this.setState(prevState => { return { users: prevState.users.filter(user => user.id !== id) }; }); }; renderUser = user => { return <User key={user.id} user={user} onClick={this.deleteUser} />; } render() { return ( <div> <h1>Users</h1> <ul> {this.state.users.map(this.renderUser)} </ul> </div> ); } } render(<App />, document.getElementById('root'));
在这个例子中,index.js 的 render 中没有箭头函数了。相关的数据被传到 User.js:
import React from "react"; import PropTypes from "prop-types"; // Note that the console.log below isn't called // when delete is clicked on a user. // That's because pureComponent's shallow // comparison works properly here because // the parent component isn't passing down // an arrow function (which would cause this // component to see a new function on each render) class User extends React.PureComponent { onDeleteClick = () => { // No bind needed since we can compose the relevant data for this item here this.props.onClick(this.props.user.id); }; render() { console.log(`${name} just rendered`); return ( <li> <input type="button" value="Delete" onClick={this.onDeleteClick} /> {this.props.user.name} </li> 95b6 ); } } User.propTypes = { user: PropTypes.object.isRequired, onClick: PropTypes.func.isRequired }; export default User;
在 User.js 中,onDeleteClick 调用了在 props 中传递的 onClick 函数,并传递了相应的 user.id。
当你再次点击 delete 按钮时,其他的用户再也不会调用 render 了!
总结
为了最佳性能:避免在 render 中使用箭头函数和绑定。
怎么做?提取子组件,或者直接传递数据给 HTML 元素
在
eslint中加入对
.bind方法和箭头函数的检测,以及解决之道请参考No .bind() or Arrow Functions in JSX Props (react/jsx-no-bind)
参考
React + Redux 性能优化(一):理论篇相关文章推荐
- react生命周期函数使用箭头函数,导致mobx-react问题
- React在Render中使用bind可能导致的问题
- react native 学习笔记 2016_1223 (环境,箭头函数,state设置,图片使用等)
- 关于vue组件中定时器中只能使用箭头函数的问题
- JS循环中使用bind函数的参数传递问题
- 用 @property 声明的 NSString (或 NSArray,NSDictionary) 经常使用 copy 关键字,为什么?如果改用strong关键字,可能造成什么问题?
- Boost关于bind的使用以及函数对象和传递参数的问题
- 解析vue data不可以使用箭头函数问题
- [置顶] React中一个没人能解释清楚的问题——为什么要使用Virtual DOM
- 用@property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?
- 关于在窗口消息处理函数中使用MessageBox造成消息重入的问题的研究及解决
- eslint => Jq事件绑定使用箭头函数问题
- 用@property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?
- 用@property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?
- hash_hmac函数使用不当造成的安全问题
- iOS 面试题~用@property声明NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?
- 用@property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?
- 用@property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?
- 用@property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?
- 深入理解React中何时使用箭头函数