[解決済み] なぜJSXプロップは矢印関数やバインドを使用してはいけないのですか?
質問
Reactアプリでlintを実行しているのですが、こんなエラーが出ます。
error JSX props should not use arrow functions react/jsx-no-bind
そして、ここでarrow関数を実行しています(内部は
onClick
):
{this.state.photos.map(tile => (
<span key={tile.img}>
<Checkbox
defaultChecked={tile.checked}
onCheck={() => this.selectPicture(tile)}
style={{position: 'absolute', zIndex: 99, padding: 5, backgroundColor: 'rgba(255, 255, 255, 0.72)'}}
/>
<GridTile
title={tile.title}
subtitle={<span>by <b>{tile.author}</b></span>}
actionIcon={<IconButton onClick={() => this.handleDelete(tile)}><Delete color="white"/></IconButton>}
>
<img onClick={() => this.handleOpen(tile.img)} src={tile.img} style={{cursor: 'pointer'}}/>
</GridTile>
</span>
))}
これは避けるべき悪い習慣なのでしょうか?また、どのような方法が良いのでしょうか?
どのように解決するのですか?
JSXプロップスでインライン矢印関数を使用してはいけない理由
JSXで矢印関数やバインディングを使用することは、レンダリングのたびに関数が再作成されるため、パフォーマンスに影響を与える悪い習慣です。
-
関数が作成されるたびに、前の関数はガベージ コレクションされます。多くの要素を再レンダリングすると、アニメーションに不自然さが生じる可能性があります。
-
インラインの矢印関数を使用すると
PureComponent
を使用するコンポーネントはshallowCompare
の中にshouldComponentUpdate
メソッドを使用して、とにかく再レンダリングします。矢印関数propは毎回再作成されるので、浅い比較はpropへの変更として識別し、コンポーネントは再レンダリングされます。
次の2つの例でわかるように、インラインアロー関数を使用すると
<Button>
コンポーネントが毎回再レンダリングされます (コンソールには 'render 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の提案のために ステージ3プリセット または クラスプロパティ変換 をbabelの設定に追加してください。
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>
関連
-
JavaScriptの関数この指摘の問題を説明
-
Vueの一般的な組み込みディレクティブの説明
-
[解決済み】SyntaxError: JSONの位置1に予期しないトークンoがある。
-
[解決済み】(Google Map API) Geocodeは以下の理由で成功しませんでした。REQUEST_DENIED
-
[解決済み] JavaScriptの「bind」メソッドの使い方を教えてください。
-
[解決済み] アロー関数」と「ファンクション」は同じものですか?
-
[解決済み】React JSXで引用符内のpropsにアクセスする。
-
[解決済み】ES6のarrow関数構文をジェネレータで使用することはできますか?(アロー記法)
-
[解決済み】'--jsx'フラグを指定しないとJSXを使用できない。
-
[解決済み] ES6 矢印関数で return 文を使用するのはどのような場合か
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
fetch ネットワークリクエストラッパーの説明例
-
WeChatアプレット用ユニアプリによるグローバルシェアリング
-
[解決済み】リソースの読み込みに失敗した:Bind関数でサーバーが500(Internal Server Error)のステータスで応答した【非公開
-
[解決済み】JavaScriptエラー(Uncaught SyntaxError: Unexpected end of input)
-
[解決済み】ExpressJS - throw er Unhandled errorイベント
-
[解決済み】ExpressJS : res.redirect()が期待通りに動かない?
-
HTML5 LocalStorage ローカルストレージとセッションストレージの使用法
-
jq は html ページとデータを動的に分割する。
-
[解決済み] Reactで親の状態を更新するにはどうしたらいいですか?
-
[解決済み] React - uncaught TypeError: 未定義のプロパティ 'setState' を読み取れない