J'ai un composant React qui reçoit les accessoires du magasin redux à chaque seconde. Le nouvel état a un tableau différent du dernier tableau. Pour être précis, chaque seconde un élément est ajouté au tableau. Par exemple: dans un état, le tableau est:
[1, 2, 3, 4, 5, 6]
l'état suivant
[1, 2, 3, 4, 5, 6, 7]
Mon réducteur:
return {
...state,
myList: [ payload, ...state.myList.filter(item => payload.id !== item.id).slice(0, -1) ]
}
Maintenant, dans mon composant React, je souscris à cet état et pour chaque modification, la liste est restituée.
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);
Dans 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;
Maintenant, mon problème est: il est coûteux pour moi de restituer chaque élément qui a déjà été rendu. Le MyRow utilise fortement des composants de style et d'autres opérations coûteuses. Cela provoque React pour restituer la liste entière toutes les secondes lorsque l'état est mis à jour. Cela devient pire si les mises à jour arrivent en moins de 1 seconde, comme 4 mises à jour par seconde. L'application react se bloque simplement dans ce cas.
Existe-t-il un moyen d'ajouter uniquement l'élément nouvellement ajouté à la liste et de ne pas restituer la liste entière?
Merci
Vous utilisez PureComponent , qui fait une comparaison superficielle, puis votre composant MyRow
ne doit pas être rendu sur chaque nouvel élément ajouté ( Veuillez suivre mon exemple de code ci-dessous).
Existe-t-il un moyen d'ajouter uniquement l'élément nouvellement ajouté à la liste et de ne pas restituer la liste entière?
Selon votre question - Oui, l'utilisation de PureComponent
ne devrait rendre qu'une seule fois le nouvel élément :
Voici ce que les documents de React disent:
Si la fonction render () de votre composant React rend le même résultat avec les mêmes accessoires et le même état, vous pouvez utiliser React.PureComponent pour améliorer les performances dans certains cas.
PureComponent
:Vous pouvez consulter l'exemple de code que j'ai fait pour vous.
Vous verrez que le composant Item
est toujours rendu une seule fois, car nous utilisons React.PureComponent
. Pour prouver ma déclaration, chaque fois que le Item
est rendu, j'ai ajouté l'heure actuelle du rendu. Dans l'exemple, vous verrez que le Item
Rendered at:
le temps est toujours le même, car il n'est rendu qu'une seule fois .
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
, veuillez découvrir quelle est la raison de la re-tendance, car cela ne devrait pas se produire, en raison de l'utilisation de PureComponent
. myList: [ ...state.myList, payload ]
key
à votre composant d'article <MyRow key={list.id} data={list} />
. Si les accessoires key
ou data
sont modifiés, le composant sera à nouveau rendu.Voici quelques autres bibliothèques, elles représentent un rendu efficace des listes. Je suis sûr qu'ils nous donneront des alternatives ou des idées:
PureComponent comparera superficiellement les accessoires et l'état. Donc, je suppose que les éléments sont en quelque sorte de nouveaux objets que les précédents accessoires passés, donc le rendu.
Je conseillerais, en général, de ne passer que des valeurs primitives dans des composants purs:
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>
);
}
}
Il existe une solution assez simple pour cela. React VDOM est juste un algorithme différent. Le seul élément manquant avec votre JSX est quelque chose appelé clé qui est comme un id que l'algo différent utilise et rend l'élément particulier. Il suffit de marquer l'élément avec une clé quelque chose comme ça https://reactjs.org/docs/lists-and-keys.html#keys
<li key={number.toString()}>
{number} </li>
Le problème existe vraiment dans le réducteur.
myList: [ payload, ...state.myList.filter(item => payload.id !== item.id).slice(0, -1) ]
Quelle est la logique implémentée à l'aide de la tranche (0, -1)?
C'est le coupable ici.
D'après votre question, j'ai compris que le prochain état après [1,2,3] sera [1,2,3,4].
Mais votre code donnera [4,1,2], puis [5,4,1] puis [6,5,4].
Maintenant, tous les éléments de l'état sont nouveaux, pas à l'état initial. Voir l'état n'est pas seulement ajouté, il change complètement.
Veuillez voir si vous obtenez le résultat souhaité en évitant la découpe.
myList: [ payload, ...state.myList.filter(item => payload.id !== item.id)]