web-dev-qa-db-ja.com

React / Reduxは毎秒更新されるリストをレンダリングします

Reduxストアから毎秒プロップを受け取る反応コンポーネントがあります。新しい状態には、最後の配列とは異なる配列があります。具体的には、1秒ごとに要素が配列に追加されます。例:ある状態では、配列は次のとおりです。

[1、2、3、4、5、6]

次の状態

[1、2、3、4、5、6、7]

私の減速機:

return {
  ...state,
  myList: [ payload, ...state.myList.filter(item => payload.id !== item.id).slice(0, -1) ]
}

今、私のreactコンポーネントではこの状態にサブスクライブしており、すべての変更に対してリストが再レンダリングされています。

import React, { Component } from 'react';
import MyRow from './MyRow';

class MyList extends Component {

    render() {

        return (

        <div>

            {this.props.myList.map((list, index) => (
                <MyRow key={list.id} data={list}/>
            ))}

        </div>

        );
    }
}

function select({ myList }) {
    return { myList };
}

export default connect(select)(MyList);

MyRow.jsで

import { PureComponent } from 'react';

class MyRow extends PureComponent {

    render() {

    const data = this.props.data;

        return (
            <div>
                {data.id} - {data.name}
            </div>
        );

    }
}
export default MyRow;

さて、私の問題は次のとおりです。すでにレンダリングされたすべての要素を再レンダリングするのはコストがかかります。 MyRowは、スタイル付きコンポーネントやその他の高価な操作を頻繁に使用します。これにより、状態が更新されると、反応してリスト全体が毎秒再レンダリングされます。これは、1秒あたり4回の更新のように、更新が1秒未満の場合に最悪になります。この場合、reactアプリは単にクラッシュします。

リストに新しく追加されたアイテムのみを追加し、リスト全体を再レンダリングしない方法はありますか?

ありがとう

17
Reza

浅い比較を行う PureComponent を使用している場合、コンポーネントMyRownotで再レンダリングされる必要があります新しいアイテムが追加されるたびに(以下のコード例に従ってください)。

新しく追加されたアイテムをリストに追加するだけで、リスト全体を再レンダリングしない方法はありますか?

あなたの質問によると-はい、PureComponentを使用すると新しいアイテムが1回だけレンダリングされます

Reactのドキュメント の内容は次のとおりです。

Reactコンポーネントのrender()関数が同じ小道具と状態で同じ結果をレンダリングする場合、場合によってはパフォーマンスを向上させるためにReact.PureComponentを使用できます。

PureComponentのコード例:

私があなたのためにしたコードサンプルをチェックアウトできます。

React.PureComponentを使用しているため、Itemコンポーネントが常に1回だけレンダリングされることがわかります。ステートメントを証明するために、Itemがレンダリングされるたびに、レンダリングの現在時刻を追加しました。この例から、ItemRendered at:時間は常に同じであることがわかります。1時間だけレンダリングされるため。

const itemsReducer = (state = [], action) => {
  if (action.type === 'ADD_ITEM') return [ ...state, action.payload]

  return state
}

const addItem = item => ({
  type: 'ADD_ITEM',
  payload: item
})

class Item extends React.PureComponent {
  render () {
    // As you can see here, the `Item` is always rendered only 1 time,
    // because we use `React.PureComponent`.
    // You can check that the `Item` `Rendered at:` time is always the same.
    // If we do it with `React.Component`,
    // then the `Item` will be rerendered on each List update.
    return <div>{ this.props.name }, Rendered at: { Date.now() }</div>
  }
}

class List extends React.Component {
  constructor (props) {
    super(props)
    this.state = { intervalId: null }
    this.addItem = this.addItem.bind(this)
  }

  componentDidMount () {
    // Add new item on each 1 second,
    // and keep its `id`, in order to clear the interval later
    const intervalId = setInterval(this.addItem, 1000)
    this.setState({ intervalId })
  }

  componentWillUnmount () {
    // Use intervalId from the state to clear the interval
    clearInterval(this.state.intervalId)
  }

  addItem () {
    const id = Date.now()
    this.props.addItem({ id, name: `Item - ${id}` })
  }

  renderItems () {
    return this.props.items.map(item => <Item key={item.id} {...item} />)
  }

  render () {
    return <div>{this.renderItems()}</div>
  }
}

const mapDispatchToProps = { addItem }
const mapStateToProps = state => ({ items: state })
const ListContainer = ReactRedux.connect(mapStateToProps, mapDispatchToProps)(List)

const Store = Redux.createStore(itemsReducer)
const Provider = ReactRedux.Provider

ReactDOM.render(
  <Provider store={Store}>
    <ListContainer />
  </Provider>,
  document.getElementById('container')
)
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.0/redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/5.0.7/react-redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/6.26.0/polyfill.min.js"></script>

<div id="container">
    <!-- This element's contents will be replaced with your component. -->
</div>

ソリューション:

  1. パフォーマンスの問題の原因がMyRowの再描画である場合は、PureComponentの使用により発生しないはずなので、再描画の理由を調べてください。
    • チェック/デバッグするために、reducerを単純化して、問題の原因となっているリデューサーであるかどうかを確認できます。たとえば、リストに新しいアイテムを追加するだけです(フィルター処理、スライスなど、他に何もせずに):myList: [ ...state.myList, payload ]
    • 必ず同じkeyをアイテムコンポーネント<MyRow key={list.id} data={list} />に渡すようにしてください。 keyまたはdataプロパティが変更された場合、コンポーネントは再レンダリングされます。

  1. 以下は他のライブラリです。これらはリストの効率的なレンダリングを表しています。彼らは私たちにいくつかの代替案や洞察を与えると確信しています:

    • react-virtualized -大きなリストと表形式のデータを効率的にレンダリングするためのコンポーネントを反応させる
    • react-infinite -UITableViewに基づいたブラウザ対応の効率的なスクロールコンテナ

10
Jordan Enev

PureComponentは、小道具と状態を浅く比較します。したがって、ここでの私の推測は、アイテムが以前に渡された小道具よりも何らかの形で新しいオブジェクトであるため、再レンダリングされることです。

一般に、純粋なコンポーネントのプリミティブ値のみを渡すことをお勧めします。

class MyList extends Component {
    render() {
        return (
            <div>
                {this.props.myList.map((item, index) => (
                    <MyRow key={item.id} id={item.id} name={data.name} />
                    //or it's alternative 
                    <MyRow key={item.id} {...item} />
                ))}
            </div>
        );
    }
}

//...

class MyRow extends PureComponent {
    render() {
        const {id, name} = this.props;
        return (
            <div>
                {id} - {name}
            </div>
        );
    }
}
2
Logar

これには非常に簡単な解決策があります。 React VDOMは単なる差分アルゴリズムです。JSXにない唯一の部分は、keyと呼ばれるものです。 diffing algoが特定の要素を使用してレンダリングするid。要素に次のようなKEYをタグ付けするだけ https://reactjs.org/docs/lists-and-keys.html#keys

<li key={number.toString()}>
    {number}   </li>
2
karthik

問題は実際に減速機に存在します。

myList: [ payload, ...state.myList.filter(item => payload.id !== item.id).slice(0, -1) ]

Slice(0、-1)を使用して実装されるロジックは何ですか?

ここが犯人です。

あなたの質問から、[1,2,3]の後の次の状態は[1,2,3,4]になることがわかりました。

しかし、コードは[4,1,2]、[5,4,1]、[6,5,4]の順になります。

これで、初期状態ではなく、状態のすべての要素が新しくなりました。状態は単に追加されるだけでなく、完全に変化しています。

スライスを避けて、目的の結果が得られるかどうかを確認してください。

 myList: [ payload, ...state.myList.filter(item => payload.id !== item.id)]
1
Sreeragh A R