Reactコンポーネントレンダリングの最適化 {🚀}
MobXは非常に高速で、多くの場合Reduxよりも高速ですが、ReactとMobXを最大限に活用するためのいくつかのヒントを以下に示します。ほとんどは一般的なReactに適用され、MobX特有のものではありません。これらのパターンを認識しておくことは良いことですが、通常、アプリケーションはこれらのことを全く気にしなくても十分に高速に動作します。
パフォーマンスは、実際に問題が発生した場合にのみ優先順位を付けましょう!
小さなコンポーネントをたくさん使いましょう
observer
コンポーネントは、使用するすべての値をトラッキングし、それらのいずれかが変更されると再レンダリングします。そのため、コンポーネントが小さければ小さいほど、再レンダリングする必要がある変更も小さくなります。つまり、ユーザーインターフェースのより多くの部分が、互いに独立してレンダリングされる可能性があります。
リストは専用のコンポーネントでレンダリングしましょう
これは、特に大きなコレクションをレンダリングする場合に当てはまります。Reactは、大きなコレクションをレンダリングするのが非常に苦手です。リコンサイラーは、コレクションの変更ごとに、コレクションによって生成されたコンポーネントを評価する必要があるからです。そのため、コレクションをマップしてレンダリングするだけのコンポーネントを作成し、それ以外の何もレンダリングしないことをお勧めします。
悪い例
const MyComponent = observer(({ todos, user }) => (
<div>
{user.name}
<ul>
{todos.map(todo => (
<TodoView todo={todo} key={todo.id} />
))}
</ul>
</div>
))
上記のリストでは、user.name
が変更された場合、Reactは不要にすべてのTodoView
コンポーネントをリコンサイルする必要があります。再レンダリングされませんが、リコンサイルプロセス自体がコストがかかります。
良い例
const MyComponent = observer(({ todos, user }) => (
<div>
{user.name}
<TodosView todos={todos} />
</div>
))
const TodosView = observer(({ todos }) => (
<ul>
{todos.map(todo => (
<TodoView todo={todo} key={todo.id} />
))}
</ul>
))
配列のインデックスをキーとして使用しないでください
配列のインデックスや、将来変更される可能性のある値をキーとして使用しないでください。必要に応じて、オブジェクトのIDを生成してください。このブログ記事を参照してください。
値の参照解除は遅らせる
mobx-react
を使用する場合は、できるだけ遅く値を参照解除することをお勧めします。これは、MobXがオブザーバブルな値を参照解除するコンポーネントを自動的に再レンダリングするためです。これがコンポーネントツリーの深い部分で発生すると、再レンダリングする必要があるコンポーネントが少なくなります。
遅い例
<DisplayName name={person.name} />
速い例
<DisplayName person={person} />
速い例では、name
プロパティの変更はDisplayName
の再レンダリングのみをトリガーしますが、遅い例では、コンポーネントのオーナーも再レンダリングする必要があります。これ自体は間違ってはいません。オーナーコンポーネントのレンダリングが十分に高速であれば(通常はそうである!)、このアプローチはうまく機能します。
関数プロップス {🚀}
値の参照解除を遅らせるには、多くの小さなオブザーバーコンポーネントを作成する必要があることに気付くかもしれません。それぞれがデータの異なる部分をレンダリングするようにカスタマイズされています。
const PersonNameDisplayer = observer(({ person }) => <DisplayName name={person.name} />)
const CarNameDisplayer = observer(({ car }) => <DisplayName name={car.model} />)
const ManufacturerNameDisplayer = observer(({ car }) =>
<DisplayName name={car.manufacturer.name} />
)
異なる形状の大量のデータがある場合、これはすぐに面倒になります。代替案として、*Displayer
がレンダリングするデータ返す関数を使用できます。
const GenericNameDisplayer = observer(({ getName }) => <DisplayName name={getName()} />)
次に、コンポーネントを次のように使用できます。
const MyComponent = ({ person, car }) => (
<>
<GenericNameDisplayer getName={() => person.name} />
<GenericNameDisplayer getName={() => car.model} />
<GenericNameDisplayer getName={() => car.manufacturer.name} />
</>
)
このアプローチにより、GenericNameDisplayer
をアプリケーション全体で再利用して任意の名前をレンダリングすることが可能になり、コンポーネントの再レンダリングを最小限に抑えることができます。