React-window: リストでDOM要素が再レンダリングされないようにして、cssアニメーションを有効にします

作成日 2019年06月13日  ·  7コメント  ·  ソース: bvaughn/react-window

スタックオーバーフローの問題について説明しました。

https://stackoverflow.com/questions/56588001/prevent-dom-element-re-render-in-virtualized-tree-component-using-react-window

この問題の解決策はありますか?

最も参考になるコメント

_(私はStackOverflowで回答し、ここに回答をコピーして、GoogleまたはGitHubの検索からここにたどり着く人々を支援します。ところでこの問題は#94の重複です。)_

@ acerola1 -react-windowがリスト内のすべてのアイテムを再レンダリングしないようにするには、次のことを行う必要があります。

  1. 行レンダリング機能をメモ化する
  2. その関数を親関数のスコープ外に移動します
  3. itemDataプロップを使用して、親コンポーネントと行ごとのレンダリング関数の間で通信します。

これは、react-windowの例のサンドボックスで使用されている戦略です: https

コードを使用した例を次に示します: https

useMemoを使用してそれを行うより簡単な方法があるかもしれませんが、私はそれを理解できませんでした。

Chrome開発ツールを使用してDOMノードを調べると、1つのノードが展開されたときに、ツリー全体に対してDOMノードが再作成されていないことがわかります。 これにより、新しいノードを選択するときに以前に表示されていたちらつきがなくなります(Chromeでは、Firefoxでは表示されません)。

ただし、回転アニメーションが正しく機能しない原因となるものもあります。 上でリンクしたCodeSandboxの例では、 borderプロパティにいくつかの醜いアニメーションを追加して、一部のCSSプロパティのアニメーションがどのように機能するかを示していますが、 transform CSSプロパティのアニメーションは機能していません。 この問題はreact-windowとは関係がないと思われるので、変換がアニメーション化されていないのに他のプロパティがアニメーション化されている理由を理解するために、個別にデバッグすることをお勧めします。

全てのコメント7件

申し訳ありませんが、これを閉じます。 GHの問題は、Stack Overflowの質問を宣伝するためではなく、バグレポートと機能リクエストについて話し合うためのものです。

_(私はStackOverflowで回答し、ここに回答をコピーして、GoogleまたはGitHubの検索からここにたどり着く人々を支援します。ところでこの問題は#94の重複です。)_

@ acerola1 -react-windowがリスト内のすべてのアイテムを再レンダリングしないようにするには、次のことを行う必要があります。

  1. 行レンダリング機能をメモ化する
  2. その関数を親関数のスコープ外に移動します
  3. itemDataプロップを使用して、親コンポーネントと行ごとのレンダリング関数の間で通信します。

これは、react-windowの例のサンドボックスで使用されている戦略です: https

コードを使用した例を次に示します: https

useMemoを使用してそれを行うより簡単な方法があるかもしれませんが、私はそれを理解できませんでした。

Chrome開発ツールを使用してDOMノードを調べると、1つのノードが展開されたときに、ツリー全体に対してDOMノードが再作成されていないことがわかります。 これにより、新しいノードを選択するときに以前に表示されていたちらつきがなくなります(Chromeでは、Firefoxでは表示されません)。

ただし、回転アニメーションが正しく機能しない原因となるものもあります。 上でリンクしたCodeSandboxの例では、 borderプロパティにいくつかの醜いアニメーションを追加して、一部のCSSプロパティのアニメーションがどのように機能するかを示していますが、 transform CSSプロパティのアニメーションは機能していません。 この問題はreact-windowとは関係がないと思われるので、変換がアニメーション化されていないのに他のプロパティがアニメーション化されている理由を理解するために、個別にデバッグすることをお勧めします。

非常に役立つ応答。 ありがとう、@ justingrant!

こんにちは@ bvaughn-喜んでお手伝いします。 私は同じ問題に遭遇し、答えを見つけるのに多くの時間を費やしたので、次の犠牲者のためにパン粉を残したかった。 好奇心から、 Row関数にメモ化がないのに、Reactの調整によってこれらのDOMノードが再作成される理由を知っていますか? 各行が再レンダリングされたとしても、同じコンテンツがDOMに出力されている場合、調整がうまくいかず、操作ができなくなるのはなぜですか?

たとえば、メモ化の例のアプローチを使用すると、 itemDataを変更しても(これによりList全体が再レンダリングされます)、デルタのみがDOMに適用されます。 ただし、OPの元のコードでは、 List再レンダリングされるたびに、 List内のすべてのDOMノードが再作成されます。 なぜ動作が違うのですか? react-windowのコンテキストでこの質問に興味がありますが、Reactがどのように機能するかについてもっと基本的なことを誤解しているのではないかと思います。

簡単な答え:インライン関数は、レンダリングごとに_新しい要素タイプ_を作成します。 Reactは、新しいタイプの下にあるツリー全体をアンマウントして再マウントすることでこれに応答します(以前にFooコンポーネントをレンダリングした場所でBarコンポーネントをレンダリングした場合と同じように)。 これは、ツリー内のノードを_create_または_update_するかどうかを決定するときにReactがどのように機能するかの一部にすぎません。 (これは、型が関数コンポーネントではなくクラスコンポーネントであると想像すると、少し考えやすくなる可能性があります。Reactがで作成された_instance of Foo _を再利用しようとするとは思わないでしょう。別ので作成された_インスタンスBar _の出力用の1つのレンダリング。)

これが、react-windowのドキュメントサイトのすべての例がインライン関数の表示を避けている理由です。 (これは、実際にパフォーマンスにとって重要なケースの1つです。)

そうは言っても、ここでの「小さな」ミスはパフォーマンスに比較的大きな影響を与える可能性があるため、このAPI設計は、最終的にv2の実行を検討するときに再検討する決定かもしれません。 セバスチャンと私は、今日の昼食時にこの一般的なAPIについて話していました。 それは私が推測するより多くの考えを与える必要があるものです。 どちらのアプローチにも賛否両論があると思います。

関連項目:このスレッド

@ bvaughn-ああ、それで1つの謎が!== )、アンマウントと再マウントが必要であると想定します。 右?

Reactチームは、ネストされた関数コンポーネントが何らかの方法でDOM調整に参加できるように、この動作を変更することを検討しましたか? 既存の問題を検索しましたが、一致するものが見つかりませんでした。 現在の動作はクラスコンポーネントの動作と一致していることは知っていますが、インライン関数が頻繁に使用されているため、インラインクラスは実際には重要ではありません。 問題が余分なレンダリングを引き起こしただけの場合、それは大したことではありませんが、調整を破ることははるかに影響力があります:非DOMレンダリングよりも10倍以上遅く、点滅や視覚的なジャンクを引き起こす可能性があり、CSSトランジションとアニメーションを壊します。

また、ネストされた関数コンポーネントは、フックの使用が増えるにつれておそらく一般的になるでしょう。なぜなら、コンポーネントをネストすると、ネストされたコンポーネントをモジュールスコープにリファクタリングするよりもはるかに簡単なフックデータを(クロージャを介して)使用できるからです。

動作を変更することが現実的でない場合、ESLintルールはこのケースについて警告できますか?

多くの開発者のメンタルモデルがおそらく私のようなものであったことを考えると、将来の変更の可能性に関係なく、現在の動作はドキュメントにある重要な情報のようです。Reactはすべての場合にDOMを比較します。 どう思いますか? あなたがそれが役立つと思うなら、私はいくつかのコンテンツをPRすることができます。 このようなもの:

機能コンポーネントの調整

Reactは、前のレンダリングでまったく同じ関数( f1 === f2 )が使用された場合にのみ、関数コンポーネントのDOMノードを調整します。 関数が異なる場合、Reactは、 <Foo />クラスコンポーネントインスタンスが<Bar />クラスコンポーネントインスタンスと異なるのと同じように、コンポーネントを異なるタイプと見なします。 2つの関数が同じDOMノードを出力する場合でも、Reactは古い関数によってレンダリングされたすべてのDOMノードをアンマウント(破棄)し、新しい関数によってレンダリングされたDOMノードをマウント(作成)します。

残念ながら、これは、親関数の実行時にインライン関数が再作成されるため、他の関数内で作成された関数コンポーネントがDOM調整に参加しないことを意味します。 たとえば、次の関数コンポーネントを使用すると、 Parentがレンダリングされるたびに、Reactは常にChildコンポーネント内にDOMノードを再作成します。

function Parent() {
  const Child = () => <div>DOM nodes are always re-created</div>;
  return <div><Child /></div>;
}

レンダリング中にDOMノードを再作成することはしばしば悪いことです。 調整されたレンダリングよりもはるかに遅く、視覚的なジャンクや点滅を引き起こしたり、CSSトランジションやその他のアニメーションを壊したりする可能性があります。 関数コンポーネントがDOM調整に確実に参加するようにするには、関数を直接呼び出すことができます。

return <div>{ Child() }</div>; // Good. Child DOM nodes are reconciled.
return <div><Child /></div>;  // Bad. Child DOM nodes are always re-created.

または、コンポーネントをモジュールスコープにリファクタリングできます。

function Child() {
  return <div>DOM nodes are never re-created</div>
}
function Parent() {
  return <div><Child /></div>
}

関数の定義方法は重要ではないことに注意してください。 以下の関数はいずれも、レンダリング間で調整されません。

function Parent() {
  const Child1 = () => <div>DOM nodes are always re-created</div>;
  const Child2 = function () { return <div>DOM nodes are always re-created</div>; }
  function Child3() { return <div>DOM nodes are always re-created</div>; }
  return <div><Child1 /><Child2 /><Child3 /></div>;
}

React.memoが役立つと思うかもしれませんが、親関数が実行されるたびに関数が再メモ化される場合、メモ化は役に立ちません。

// Correct.  Child1 will always be the same function.
const Child1 = React.memo (() => {
  return <div>DOM nodes are never re-created</div>
});
function Parent() {
  // Wrong. Every time Parent renders, Child2 will be a different function.
  const Child2 = React.memo(() => <div>DOM nodes are always re-created</div>);
  return <div><Child1 /><Child2 /></div>;
}

最後に、ヒント:上記の問題を防ぐためにリファクタリングする必要のあるインライン関数コンポーネントを特定するには、JSXファイルで、名前が大文字で始まる関数のインデントされた宣言を検索します。 次のような正規表現を使用できます: \s\s(const|var|let|function) [A-Z][a-z ]

@justingrantこれは非常に役立ち、ドキュメントで強調表示する必要があります。

私にとって(そして、多くの人がこの間違いを犯すかもしれないと確信しています)-私は行レンダリング関数を正しくメモしていましたが、それは親関数と同じスコープでした。

このページは役に立ちましたか?
0 / 5 - 0 評価