J'ai une table qui affiche les contacts et je veux trier les contacts par prénom. Le tableau de contacts provient du magasin redux, qui passera ensuite par les accessoires, mais je veux que l'état local détienne la manière dont ces contacts sont triés, car il s'agit de l'état de l'interface utilisateur locale. Comment puis-je y arriver? Jusqu'ici, j'ai placé des contacts dans componentWillReceiveProps
, mais pour une raison quelconque, il ne reçoit pas les accessoires lorsqu'il change. Comment mettre à jour l'état local chaque fois que l'état du magasin redux change?
const Table = React.createClass({
getInitialState () {
return {contacts: []}
},
componentWillReceiveProps () {
this.setState({ contacts: this.props.data.contacts})
},
sortContacts (parameter, e){
...
},
render () {
return (
<table>
<thead>
<tr>
<th onClick={this.sortContacts.bind(this, "firstName")}>First Name</th>
</tr>
</thead>
<tbody>
{contactRows}
</tbody>
</table>
)
}
})
mise à jour du code actuel incluant le filtrage
import React, {Component} from 'react'
import TableRow from './TableRow'
class Table extends Component {
constructor (props) {
super(props)
this.state = { sortBy: "fistName" }
}
sortContacts (parameter) {
console.log('in sortContacts')
this.setState({ sortBy: parameter })
}
sortedContacts () {
console.log('in sortedContacts')
const param = this.state.sortBy
return (
this.props.data.contacts.sort(function (a, b){
if (!a.hasOwnProperty(param)){
a[param] = " ";
}
if (!b.hasOwnProperty(param)){
b[param] = " ";
}
const nameA = a[param].toLowerCase(), nameB = b[param].toLowerCase();
if (nameA > nameB) {
return 1;
} else {
return -1;
}
})
)
}
filteredSortedContacts () {
console.log('in filteredSortedContacts')
const filterText = this.props.data.filterText.toLowerCase()
let filteredContacts = this.sortedContacts()
if (filterText.length > 0) {
filteredContacts = filteredContacts.filter(function (contact){
return (
contact.hasOwnProperty('lastName') &&
contact.lastName.toLowerCase().includes(filterText)
)
})
}
return filteredContacts
}
contactRows () {
console.log('in contactRows')
return this.filteredSortedContacts().map((contact, idx) =>
<TableRow contact={contact} key={idx}/>
)
}
render () {
return (
<div className="table-container">
<table className="table table-bordered">
<thead>
<tr>
<th className="th-cell" onClick={this.sortContacts.bind(this, "firstName")}>First Name</th>
<th onClick={this.sortContacts.bind(this, "lastName")}>Last Name</th>
<th>Date of Birth</th>
<th>Phone</th>
<th>Email</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
{this.contactRows()}
</tbody>
</table>
</div>
)
}
}
export default Table
Le problème que je vois maintenant, c'est que contactRows, filteredSortedContacts, sortedContacts
sont appelés plusieurs fois, une fois pour chaque table. Je ne vois pas comment cela peut se produire si j'appelle seulement contactRows
une fois dans le corps.
Votre approche consistant à utiliser à la fois le magasin redux et le magasin local est correcte.
Il suffit de ne pas essayer de dupliquer l’état du magasin Redux dans votre composant. Continuez à vous y référer via des accessoires.
Créez plutôt une fonction sortedContacts
qui calcule une valeur à la volée en appliquant le paramètre sortBy
stocké localement aux contacts stockés en redux.
const Table extends React.Component {
constructor(props) {
super(props);
this.state = {
sortBy: 'id' // default sort param
}
}
sortContacts(param) {
this.setState({ sortBy: param})
}
sortedContacts() {
return this.props.contacts.sort(...); // return sorted collection
}
render() {
return (
<table>
<thead>
<tr>
<th onClick={() => this.sortContacts("firstName")}>First Name</th>
</tr>
</thead>
<tbody>
{this.sortedContacts()}
</tbody>
</table>
)
}
}
La méthode componentWillReceiveProps()
n'est pas appelée pour le rendu initial. Que pouvez-vous faire si vous souhaitez uniquement utiliser les données des accessoires comme données initiales, par exemple:
getInitialState () {
return {
contacts: this.props.data.contacts
}
}
Dans les documents Réagir , ils suggèrent de nommer les accessoires initialContacts, juste pour préciser que le seul but des accessoires est d'initialiser quelque chose en interne.
Maintenant, si vous voulez qu'il se mette à jour lorsque this.props.contacts
change, vous pouvez utiliser componentWillReceiveProps()
comme vous l'avez fait. Mais je ne suis pas sûr que ce soit la meilleure idée. De la docs:
L'utilisation d'accessoires pour générer un état dans getInitialState conduit souvent à duplication de "source de vérité", c’est-à-dire où se trouvent les données réelles. C'est parce que getInitialState n'est appelé que lorsque le composant est premier créé.
Autant que possible, calculez les valeurs à la volée pour vous assurer qu'elles ne le font pas désynchroniser plus tard et causer des problèmes de maintenance.
React 16.3 inclut une fonction statique getDerivedStateFromProps(nextProps, prevState)
, qui sera invoquée après le montage initial ainsi que lors de la réception de nouveaux accessoires. Voir https://reactjs.org/docs/react-component.html#static-getderivedstatefromprops