React の Render でアロー関数とバインドを使用すると問題が発生する理由

原文转自https://zhuanlan.zhihu.com/p/29266705

在 render 中使用箭头函数或绑定会导致子组件重新渲染,即使 state 并没有改变。作者推荐使用提取子组件或在 HTML 元素中传递数据的方式来避免绑定。
この例では、レンダリングでアロー関数を使用して、ユーザー ID を各削除ボタンにバインドします。

CodeSandboxをクリックして、完全なデモを表示してデモンストレーションします。(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 が呼び出されるたびに、ログがコンソールに出力されます。ユーザーは PureComponent として宣言されています。したがって、ユーザーはプロパティまたは状態が変更された場合にのみ再レンダリングする必要があります。ただし、削除ボタンをクリックすると、ユーザー インスタンスごとにレンダリングが呼び出されます

その理由は、親コンポーネントが props でアロー関数を渡すためです。アロー関数は、(bind を使用するのと同じ方法で) レンダリングされるたびに再割り当てされますしたがって、User を PureComponent として宣言したとしても、User の親コンポーネントのアロー関数により、User コンポーネントは新しい関数をすべての User インスタンスに渡します。したがって、削除ボタンをクリックすると、すべてのユーザー インスタンスが再レンダリングされます。

結論は:

レンダリングではアロー関数とバインディングを使用しないでください。そうしないと、 shouldComponentUpdate と PureComponent のパフォーマンスの最適化が中断されます。

何をすべきでしょうか?

まず、レンダリングでアロー関数を使用しない場合の違いを比較する例を見てみましょう。

CodeSandboxをクリックして、完全なデモを表示および実行します。

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 のレンダリングにアロー関数はありません。関連するデータが 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>
    );
  }
}

User.propTypes = {
  user: PropTypes.object.isRequired,
  onClick: PropTypes.func.isRequired
};

export default User;

User.js では、onDeleteClick は props で渡された onClick 関数を呼び出し、対応する user.id を渡します。

削除ボタンを再度クリックすると、他のユーザーが再度 render を呼び出すことはなくなります。

要約する

最高のパフォーマンスを得るには:

  1. レンダリングではアロー関数とバインディングを使用しないでください。
  2. どうやってするの?子コンポーネントを抽出するか、データを HTML 要素に直接渡します。
  3. eslintペア.bind方法和箭头函数とソリューションの検出を追加するには、No.bind() または JSX Props のアロー関数 (react/jsx-no-bind)を参照してください。
参考

React + Redux パフォーマンスの最適化 (1): 理論

おすすめ

転載: blog.csdn.net/luo1055120207/article/details/79025365