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アプリは単にクラッシュします。
リストに新しく追加されたアイテムのみを追加し、リスト全体を再レンダリングしない方法はありますか?
ありがとう
浅い比較を行う PureComponent を使用している場合、コンポーネントMyRow
はnotで再レンダリングされる必要があります新しいアイテムが追加されるたびに(以下のコード例に従ってください)。
新しく追加されたアイテムをリストに追加するだけで、リスト全体を再レンダリングしない方法はありますか?
あなたの質問によると-はい、PureComponent
を使用すると新しいアイテムが1回だけレンダリングされます:
Reactのドキュメント の内容は次のとおりです。
Reactコンポーネントのrender()関数が同じ小道具と状態で同じ結果をレンダリングする場合、場合によってはパフォーマンスを向上させるためにReact.PureComponentを使用できます。
PureComponent
のコード例:私があなたのためにしたコードサンプルをチェックアウトできます。
React.PureComponent
を使用しているため、Item
コンポーネントが常に1回だけレンダリングされることがわかります。ステートメントを証明するために、Item
がレンダリングされるたびに、レンダリングの現在時刻を追加しました。この例から、Item
Rendered 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>
MyRow
の再描画である場合は、PureComponent
の使用により発生しないはずなので、再描画の理由を調べてください。myList: [ ...state.myList, payload ]
key
をアイテムコンポーネント<MyRow key={list.id} data={list} />
に渡すようにしてください。 key
またはdata
プロパティが変更された場合、コンポーネントは再レンダリングされます。以下は他のライブラリです。これらはリストの効率的なレンダリングを表しています。彼らは私たちにいくつかの代替案や洞察を与えると確信しています:
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>
);
}
}
これには非常に簡単な解決策があります。 React VDOMは単なる差分アルゴリズムです。JSXにない唯一の部分は、keyと呼ばれるものです。 diffing algoが特定の要素を使用してレンダリングするid。要素に次のようなKEYをタグ付けするだけ https://reactjs.org/docs/lists-and-keys.html#keys
<li key={number.toString()}>
{number} </li>
問題は実際に減速機に存在します。
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)]