[react]反応技術-第11章素子性能の最適化
10章で作成した
に注意
関数は、最初のレンダー
確かに遅くなったように見えます.でも原因は何ですか?
次の場合、構成部品は再レンダリングされます.
1.自分が受け取った
2.自分の
3.親構成部品を再レンダリングする場合
4.
この場合、項目をチェックすると
構成部品の再レンダリングを防止するには、
この場合に使用できるのは
1.
2.
関数型更新の代わりに
2500個の初期データを登録しましたが、実際の画面には9つの項目しかありません.残りはスクロールして見る必要があります.スクロールする前に非表示の構成部品をプリレンダリングするのは非効率といえます.
todo-app
でデータが急増した場合、アプリケーションは遅くなるでしょう.パフォーマンスを最適化する前に、ラックに大量のデータを表示してみてください(lag
).App.js
を以下のように修正することにより、2500個のデータを生成する関数がtodos
に設定される.に注意
useState(createBulkTodos())
回レンダリングするたびに関数が呼び出されます.関数は、最初のレンダー
useState(createBulkTodos)
でのみ呼び出されます.//...
import TodoList from './components/TodoList';
function createBulkTodos() {
const array = [];
for (let i = 1; i <= 2500; i++) {
array.push({
id: i,
text: `할 일 ${i}`,
checked: false,
});
}
return array;
}
const App = () => {
const [todos, setTodos] = useState(createBulkTodos);
const nextId = useRef(2501);
const onInsert = useCallback(
//...
確かに遅くなったように見えます.でも原因は何ですか?
次の場合、構成部品は再レンダリングされます.
1.自分が受け取った
props
変更の場合2.自分の
state
が変わったとき3.親構成部品を再レンダリングする場合
4.
forceUpdate
関数を実行する場合この場合、項目をチェックすると
state
が変更され、App
要素が再レンダリングされるので、サブ要素TodoList
要素も再レンダリングされ、その中の無数の要素も再レンダリングされます.チェックされていない構成部品も再レンダリングする必要がないので、このような状況を回避するために最適化する必要があります.React.memoを使用して構成部品のパフォーマンスを最適化する
構成部品の再レンダリングを防止するには、
shouldComponentUpdate
というライフサイクルを使用します.しかし、それを使用できない関数型については、どうすればいいのでしょうか.この場合に使用できるのは
React.memo
関数です.構成部品のprops
が変更されていない場合は、これを再レンダリングしないように設定して、関数型構成部品の再レンダリング性能を最適化できます.TodoListItem.js
import React from 'react';
import {
MdCheckBoxOutlineBlank,
MdCheckBox,
MdRemoveCircleOutline,
} from 'react-icons/md';
import cn from 'classnames';
import './TodoListItem.scss';
const TodoListItem = ({ todo, onRemove, onToggle }) => {
(...)
};
export default React.memo(TodoListItem);
TodoListItem
要素をReact.memo()
にカプセル化し、todo
、onRemove
、onToggle
を変更せずにレンダリングしないようにする.ただし,現在の項目ではtodos
配列を更新するとonRemove
とonToggle
関数も更新されるため,React.memo
では不十分である.1.
useState
の関数更新setTodos
を使用する場合、新しい状態をパラメータとするよりも、関数型更新と呼ばれる状態更新の方法を定義する更新関数を加えることもできる.// 사용 예시
const [number, setNumber] = useState(0);
const onIncrease = useCallback(
() => setNumber(prevNumber => prevNumber + 1),
[],
);
setNumber(number+1)
ではなく、上記のコードのように更新方法を定義する更新関数を加える場合、useCallback
で使用する場合、2番目のパラメータ配列にnumber
を入れる必要はありません.このように彼に和弦を変えてあげます.2.
useReducer
使用関数型更新の代わりに
useReducer
を用いることも、onToggle
およびonRemove
が絶えず更新する問題を解決することができる.App.js
import React, { useReducer, useRef, useCallback }from 'react';
...
function todoReducer(todos, action) {
switch (action.type) {
case 'INSERT': // 새로 추가
// { type: ‘INSERT‘, todo: { id: 1, text: ‘todo‘, checked: false } }
return todos.concat(action.todo);
case 'REMOVE': // 제거
// { type: ‘REMOVE‘, id: 1 }
return todos.filter(todo => todo.id !== action.id);
case 'TOGGLE': // 토글
// { type: ‘REMOVE‘, id: 1 }
return todos.map(todo =>
todo.id === action.id ? { ...todo, checked: !todo.checked } : todo,
);
default:
return todos;
}
}
const App = () => {
const [todos, dispatch] = useReducer(todoReducer, undefined, createBulkTodos);
const nextId = useRef(2501);
const onInsert = useCallback( text => {
const todo = {
id: nextId.current,
text,
checked: false,
};
dispatch({ type: 'INSERT', todo });
nextId.current +=1;
}, []);
const onRemove = useCallback( id => {
dispatch({ type: 'REMOVE', id });
}, []);
const onToggle = useCallback( id => {
dispatch({ type: 'TOGGLE', id });
}, []);
return(
...
useReducer
を使用する場合は、元の2番目のパラメータに初期状態を加えます.2番目のパラメータにはundefined
が加えられ、3番目のパラメータには初期状態を生成する関数createBulkTodos
が加えられる.createBulkTodos
関数は、構成部品が最初にレンダリングされた場合にのみ呼び出されます.useReducer
を使用する方法の欠点は、多くの既存のコードを修正する必要があることであるが、その利点は、更新状態の論理を素子の外に集中させることができることである.react-仮想化によるレンダリングの最適化
2500個の初期データを登録しましたが、実際の画面には9つの項目しかありません.残りはスクロールして見る必要があります.スクロールする前に非表示の構成部品をプリレンダリングするのは非効率といえます.
react-virtualized
を使用すると、リスト要素をスクロールする前に表示されない要素をレンダリングせずに、そのサイズのみを占めることができます.react-virtualized
で提供されるList
コンポーネントを使用して最適化するには、px単位で各プロジェクトの実際のサイズを事前に理解する必要があります.これは開発者ツールで確認できます.(やるべきこと1上枠がないので下に量る2)TodoList.js
import React, { useCallback } from 'react';
import { List } from 'react-virtualized';
import TodoListItem from './TodoListItem';
import './TodoList.scss';
const TodoList = ({ todos, onRemove, onToggle }) => {
const rowRenderer = useCallback(
({ index, key, style }) => {
const todo = todos[index];
return (
<TodoListItem
todo={todo}
key={key}
onRemove={onRemove}
onToggle={onToggle}
style={style}
/>
);
},
[onRemove, onToggle, todos],
);
return (
<List
className="TodoList"
width={512} // 전체 크기
height={513} // 전체 높이
rowCount={todos.length} // 항목 개수
rowHeight={57} // 항목 높이
rowRenderer={rowRenderer} // 항목을 렌더링할 때 쓰는 함수
list={todos} // 배열
style={{ outline: 'none' }} // List에 기본 적용되는 outline 스타일 제거
/>
);
};
export default React.memo(TodoList);
List
要素を使用するには、rowRenderer
という関数を書き直す必要があります.この関数は、react-virtualized
のList
要素上で各TodoItem
をレンダリングするために使用されます.この関数は、List
要素のprops
に設定する必要があります.この関数は、パラメータのindex, key, style
値をオブジェクトタイプとして使用します.List
コンポーネントを使用する場合、このリストの全体的なサイズ、各アイテムの高さ、各アイテムをレンダリングする際に使用する関数、および配列をprops
に配置すると、渡されたprops
を使用して自動的に最適化できます.TodoListItem.js
...
const TodoListItem = ({ todo, onRemove, onToggle, style }) => {
const { id, text, checked } = todo;
return (
<div className="TodoListItem-virtualized" style={style}>
<div className="TodoListItem">
...
</div>
</div>
);
};
...
render
関数の既存のコンテンツをdiv
で囲み、div
でTodoListItem-virtualized
に設定し、className
で受信したprops
を使用する.style
既存の&+&、&:nth-child(偶数)を使用して、異なる背景色を提供するコードをクリアし、コードの上部に次のコードを挿入します..TodoListItem-virtualized {
& + & {
border-top: 1px solid #dee2e6;
}
&:nth-child(even) {
background: #f8f9fa;
}
}
Reference
この問題について([react]反応技術-第11章素子性能の最適化), 我々は、より多くの情報をここで見つけました https://velog.io/@dazzlynn/React-리액트를-다루는-기술-11장-컴포넌트-성능-최적화テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol