連絡先を表示するテーブルがあり、連絡先を名で並べ替えたい。連絡先配列はreduxストアから取得され、小道具を介して送信されますが、ローカルUIはローカルUI状態であるため、これらの連絡先の並べ替え方法をローカル状態に保持する必要があります。どうすればこれを達成できますか?これまで連絡先をcomponentWillReceiveProps
に配置しましたが、何らかの理由で、変更時に小道具を受け取りません。 reduxストアの状態が変わるたびにローカル状態を更新するにはどうすればよいですか?
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>
)
}
})
フィルタリングを含む現在のコードの更新
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
私が今見ている問題は、contactRows, filteredSortedContacts, sortedContacts
は、TableRowごとに1回、複数回呼び出されています。本文でcontactRows
を1回だけ呼び出すと、これがどのように発生するかわかりません。
Reduxストアとローカルストアの両方を使用するアプローチは正しいです。
コンポーネント内のreduxストアから状態を複製しようとしないでください。小道具でそれを参照してください。
代わりに、ローカルに保存されたsortedContacts
paramをreduxに保存された連絡先に適用することにより、その場で値を計算するsortBy
関数を作成します。
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>
)
}
}
componentWillReceiveProps()
メソッドは、最初のレンダリングでは呼び出されません。小道具からのデータのみを初期データとして使用する場合は、次のようにします。
_getInitialState () {
return {
contacts: this.props.data.contacts
}
}
_
React docs では、小道具の唯一の目的が何かを内部で初期化することであることを明確にするために、小道具にinitialContactsという名前を付けることを提案しています。
_this.props.contacts
_が変更されたときに更新したい場合は、componentWillReceiveProps()
を使用できます。しかし、それが最良のアイデアであるかどうかはわかりません。ドキュメントから:
小道具を使用してgetInitialStateで状態を生成すると、多くの場合、「真実の源」、つまり実際のデータが重複することになります。これは、コンポーネントが最初に作成されたときにのみgetInitialStateが呼び出されるためです。
可能な場合は常に値をオンザフライで計算し、後で同期がずれてメンテナンスの問題が発生しないようにします。
React 16.3には、静的関数getDerivedStateFromProps(nextProps, prevState)
が含まれています。これは、初期マウント後および新しい小道具を受け取ったときに呼び出されます。 https://reactjs.org/docs/react-component.html#static-getderivedstatefromprops を参照してください