私はReact、Redux、ImmutableJSにかなり慣れておらず、いくつかのパフォーマンスの問題に遭遇しました。
私はデータの大きなツリー構造を持っており、現在この構造にフラットリストとして保存しています。
new Map({
1: new Node({
id: 1,
text: 'Root',
children: [2,3]
}),
2: new Node({
id: 2,
text: 'Child 1',
children: [4]
}),
3: new Node({
id: 3,
text: 'Child 2',
children: []
}),
4: new Node({
id: 4,
text: 'Child of child 1',
children: []
})
});
フラットリストとして構成するとノードの更新が簡単になりますが、ツリーが大きくなるにつれて相互作用が遅くなることがわかりました。インタラクションには、1つ以上のノードの選択、子ノードの表示の切り替え、テキストの更新などが含まれます。 UIが遅くなる主な理由は、インタラクションごとにツリー全体が再描画されていることのようです。
shouldComponentUpdate
を使用して、ノード3を更新しても、ノード2と4が更新されないようにします。データがツリーとして保存されている場合(this.props !== nextProps
かどうかを簡単に確認できます)、これは簡単ですが、データがフラットリストに保存されているため、確認はかなり複雑になります。
データを保存し、shouldComponentUpdate
(または他の方法)を使用して、数百または数千のツリーノードでスムーズなUIをサポートするにはどうすればよいですか?
編集
私はストアをトップレベルで接続していて、ストア全体をサブコンポーネントに渡す必要があります。
私の構造は次のとおりです。
<NodeTree>
<NodeBranch>
<Node>{{text}}</Node>
<NodeTree>...</NodeTree>
</NodeBranch>
<NodeBranch>
<Node>{{text}}</Node>
<NodeTree>...</NodeTree>
</NodeBranch>
...
</NodeTree>
<Node>
はshouldComponentUpdate
で簡単なチェックを行ってタイトルが変更されたかどうかを確認できますが、再帰的に与えられた<NodeTree>
または<NodeBranch>
で使用する同様のソリューションはありません木の性質。
最善の解決策(@Dan Abramovに感謝)は、トップレベルで接続するのではなく、各<NodeBranch>
を接続することであるように見えます。私は今晩テストします。
I 追加したばかり それを示す新しい例。
次のように実行できます。
git clone https://github.com/rackt/redux.git
cd redux/examples/tree-view
npm install
npm start
open http://localhost:3000/
ダンアブラモフのソリューションは、通常の場合の99%で問題ありません。
ただし、各増分に必要な計算時間は、ツリー内のノード数に比例しますO(n)。これは、接続されている各ノードHOCが少しのコードを実行する必要があるためです(アイデンティティの比較...)。ただし、このコードは実行がかなり高速です。
ラップトップに10kノードを使用してDanAbramovソリューションをテストすると、カウンターをインクリメントしようとするとレイテンシーが発生し始めます(100ミリ秒のレイテンシーなど)。安価なモバイルデバイスではおそらくはるかに悪いです。
一度に1万個のアイテムをDOMでレンダリングしようとすべきではないと主張する人もいるかもしれませんが、私はそれに完全に同意しますが、本当に多くのアイテムを表示して接続したい場合は、それほど単純ではありません。
これをO(n)からO(1)に変えたい場合は、独自のconnect
HOC(Higher Order Component)サブスクリプションが、すべてのHOCのサブスクリプションではなく、基になるカウンターが更新されたときにのみトリガーされるシステム。
これを行う方法については、これで説明しました answer 。
connect
がより柔軟になり、オプションを使用してストアのサブスクリプションを簡単にカスタマイズできるように、react-reduxで issue を作成しました。アイデアは、connect
コードを再利用し、グローバルな状態の変更(つまり、store.subscribe()
)ではなく、状態スライスの変更を効率的にサブスクライブできるようにする必要があるということです。
const mapStateToProps = (state,props) => {node: selectNodeById(state,props.nodeId)}
const connectOptions = {
doSubscribe: (store,props) => store.subscribeNode(props.nodeId)
}
connect(mapStateToProps, undefined,connectOptions)(ComponentToConnect)
subscribeNode
メソッドを使用してストアエンハンサーを作成するのは、依然としてあなた自身の責任です。