JSXプロップでインライン矢印関数を使用すべきでない理由
JSXで矢印関数またはバインディングを使用することは、レンダリングごとに関数が再作成されるため、パフォーマンスを低下させる悪い習慣です。
関数が作成されるたびに、前の関数はガベージコレクションされます。多くの要素をレンダリングすると、アニメーションにジャンクが生じる可能性があります。
インライン矢印機能を使用すると発生しますPureComponent
Sを、使用することをコンポーネントshallowCompare
でのshouldComponentUpdate
方法は、とにかく再度レンダリングします。アロー関数のプロップは毎回再作成されるため、浅い比較ではプロペラへの変更として識別され、コンポーネントが再レンダリングされます。
次の2つの例からわかるように、インライン矢印関数を使用すると、<Button>
コンポーネントは毎回再レンダリングされます(コンソールには「レンダリングボタン」テキストが表示されます)。
例1- インラインハンドラーのない PureComponent
class Button extends React.PureComponent {
render() {
const { onClick } = this.props;
console.log('render button');
return (
<button onClick={ onClick }>Click</button>
);
}
}
class Parent extends React.Component {
state = {
counter: 0
}
onClick = () => this.setState((prevState) => ({
counter: prevState.counter + 1
}));
render() {
const { counter } = this.state;
return (
<div>
<Button onClick={ this.onClick } />
<div>{ counter }</div>
</div>
);
}
}
ReactDOM.render(
<Parent />,
document.getElementById('root')
);
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<div id="root"></div>
例2 - PureComponent とインラインハンドラ
class Button extends React.PureComponent {
render() {
const { onClick } = this.props;
console.log('render button');
return (
<button onClick={ onClick }>Click</button>
);
}
}
class Parent extends React.Component {
state = {
counter: 0
}
render() {
const { counter } = this.state;
return (
<div>
<Button onClick={ () => this.setState((prevState) => ({
counter: prevState.counter + 1
})) } />
<div>{ counter }</div>
</div>
);
}
}
ReactDOM.render(
<Parent />,
document.getElementById('root')
);
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<div id="root"></div>
this
矢印関数をインライン化せずにメソッドをバインドする
コンストラクターでメソッドを手動でバインドします。
class Button extends React.Component {
constructor(props, context) {
super(props, context);
this.cb = this.cb.bind(this);
}
cb() {
}
render() {
return (
<button onClick={ this.cb }>Click</button>
);
}
}
矢印関数で提案クラスフィールドを使用してメソッドをバインドします。これはステージ3の提案なので、ステージ3プリセットまたはClassプロパティ変換をバベル構成に追加する必要があります。
class Button extends React.Component {
cb = () => { // the class property is initialized with an arrow function that binds this to the class
}
render() {
return (
<button onClick={ this.cb }>Click</button>
);
}
}
内部コールバックを持つ関数コンポーネント
関数コンポーネント内に内部関数(たとえば、イベントハンドラー)を作成すると、コンポーネントがレンダリングされるたびに関数が再作成されます。関数が小道具として(またはコンテキストを介して)子コンポーネント(Button
この場合)に渡されると、その子も再レンダリングされます。
例1-内部コールバックを持つ関数コンポーネント:
const { memo, useState } = React;
const Button = memo(({ onClick }) => console.log('render button') || (
<button onClick={onClick}>Click</button>
));
const Parent = () => {
const [counter, setCounter] = useState(0);
const increment = () => setCounter(counter => counter + 1); // the function is recreated all the time
return (
<div>
<Button onClick={increment} />
<div>{counter}</div>
</div>
);
}
ReactDOM.render(
<Parent />,
document.getElementById('root')
);
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>
この問題を解決するには、コールバックをuseCallback()
フックでラップし、依存関係を空の配列に設定します。
注:useState
生成された関数は、現在の状態を提供する更新機能を受け付けます。このように、現在の状態をの依存関係に設定する必要はありませんuseCallback
。
例2-useCallbackでラップされた内部コールバックを持つ関数コンポーネント:
const { memo, useState, useCallback } = React;
const Button = memo(({ onClick }) => console.log('render button') || (
<button onClick={onClick}>Click</button>
));
const Parent = () => {
const [counter, setCounter] = useState(0);
const increment = useCallback(() => setCounter(counter => counter + 1), []);
return (
<div>
<Button onClick={increment} />
<div>{counter}</div>
</div>
);
}
ReactDOM.render(
<Parent />,
document.getElementById('root')
);
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>