0

ファイルのリストをレンダリングする React コンポーネントがあります。リストが非常に長くなる場合があり、この場合、UI の観点からはページネーションが理想的ではないため、ファイルのリストは再レンダリング中 (たとえば、ファイルをドラッグ アンド ドロップして並べ替えるとき) に非常に遅くなります。

速度低下の原因の 1 つは、ファイルごとに 1 回実行されるループに、いくつかのbind()呼び出しがあることです。

render() {
    return (
        <...>
        {this.props.files.map((file, index) => {
            return (
                <tr
                    key={`${index}#${file.name}`}
                    onDragStart={this.onDragStart.bind(this, file, index)}
                    onDragEnter={this.onDragEnter.bind(this, file)}
                    onDragOver={this.onDragOver.bind(this, file)}
                    onDragLeave={this.onDragLeave.bind(this, file)}
                    onDrop={this.onDrop.bind(this, file, index)}
                />
            );
        })}
        </...>
    );
}

これらのバインドは、ドラッグ アンド ドロップ ハンドラーがどのファイルがドラッグされ、どこにドロップされているかを認識するために必要です。これらすべてのバインドが数百のファイルごとに 1 回実行されるため (結果の要素が最適化され、実際にはレンダリングされない場合でも)、処理が少し遅くなっても不思議ではありません。

各反復で各関数に一意のバインドを作成することなく、必要な反復ごとのデータをこれらの関数に渡すために、これを行うためのより良い方法があるかどうか疑問に思っています。

私自身の回答として投稿する解決策が 1 つありますが、この解決策が良いか悪いか、および欠点があるかどうかについてフィードバックをいただければ幸いです。

4

1 に答える 1

0

したがって、私の解決策は、通常の方法で関数をコンストラクターで一度バインドし、反復ごとのデータを<tr/>DOM 要素自体に配置することでした。

関数が呼び出されると、ブラウザーは、イベント ハンドラーがアタッチされている DOM ノードを指すプロパティEventを含むオブジェクトを渡し、反復ごとのデータを再度抽出できるようにします。currentTarget

これにより、同じ関数 (コンストラクターで 1 回だけバインドされたもの) を、追加のバインドなしで複数のレンダリングを通じて何度も使用できます。

この方法の唯一の欠点は、オブジェクトを DOM 属性としてアタッチできず、文字列しかアタッチできないことです。私の場合、fileオブジェクトをドロップして数値に固執し、それを使用して、必要な場合にのみオブジェクトindexを検索しました。file

constructor() {
    // Functions are now bound only once during construction
    this.onDragStart = this.onDragStart.bind(this);
    this.onDragEnter = this.onDragEnter.bind(this);
    this.onDragOver = this.onDragOver.bind(this);
    this.onDragLeave = this.onDragLeave.bind(this);
    this.onDrop = this.onDrop.bind(this);
}

onDragStart(event) {
    // 'index' is recovered from the DOM node
    const index = parseInt(event.currentTarget.dataset.index);
    console.log('Event with index', index);

    // Get back the 'file' object (unique to my code, but showing that
    // I could not pass an object through this method and thus had to
    // retrieve it again.)
    const file = (index >= 0) ? this.props.files[index] : null;
}
// Same for other onXXX functions

// No more binds!
render() {
    return (
        <...>
        {this.props.files.map((file, index) => {
            return (
                <tr
                    key={`${index}#${file.name}`}
                    data-index={index}
                    onDragStart={this.onDragStart}
                    onDragEnter={this.onDragEnter}
                    onDragOver={this.onDragOver}
                    onDragLeave={this.onDragLeave}
                    onDrop={this.onDrop}
                />
            );
        })}
        </...>
    );
}
于 2018-12-29T12:26:17.803 に答える