我有一个react组件,每秒都会从redux商店接收道具。新状态的数组与最后一个数组不同。具体而言,每秒都会将一个元素添加到数组中。例如: 在一个状态中,数组是:
[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次更新。在这种情况下,react应用程序会崩溃。
有没有办法只将新添加的项添加到列表中而不重新呈现整个列表?
由于
答案 0 :(得分:9)
您正在使用PureComponent进行浅层比较,然后您的组件MyRow
应该不在每个要添加的新项目上重新呈现(请关注我的下面的代码示例)。
有没有办法只将新添加的项添加到列表中而不重新呈现整个列表?
根据您的问题 - 是的,使用PureComponent
只能渲染一次新项目:
以下是React's docs所说的内容:
如果React组件的render()函数在给定相同的props和state的情况下呈现相同的结果,则在某些情况下可以使用React.PureComponent来提升性能。
PureComponent
的代码示例:您可以查看我为您做的代码示例。
您将看到Item
组件始终只呈现一次,因为我们使用React.PureComponent
。为了证明我的陈述,每次渲染Item
时,我都会添加当前的渲染时间。从示例中,您会看到Item
Rendered at:
时间始终相同,因为它只呈现了一次。
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
道具,则会重新呈现该组件。 以下是一些其他库,这些库代表了列表的高效呈现。我相信他们会给我们一些替代或见解:
答案 1 :(得分:1)
问题确实存在于减速机中。
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)]
答案 2 :(得分:1)
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>
);
}
}
答案 3 :(得分:1)
这方面有一个简单的解决方案。 React VDOM只是一种差异算法。你的JSX唯一缺少的就是一个叫做 key 的东西,它就像是变异算法使用并呈现特定元素的id。只需使用类似于https://reactjs.org/docs/lists-and-keys.html#keys
的KEY标记元素<li key={number.toString()}>
{number} </li>
答案 4 :(得分:-1)
看起来你每次都在reducer中创建一个新数组,其中需要重新计算所有数组索引。您是否尝试将新节点附加到列表末尾而不是预先添加?