コードは機能しますが、ベストプラクティスの質問があります。状態にオブジェクトの配列があり、ユーザーの操作によって一度に1つのオブジェクトの値が変更されます。私の知る限り、状態を直接変更することは想定されていません。代わりに常にsetState
を使用する必要があります。それを何らかの価格で回避したい場合は、反復によって配列をディープクローンし、クローンを変更します。次に、状態をクローンに設定します。私の意見では、とにかく後で変更する状態を変更しないようにすると、パフォーマンスが低下します。
詳細バージョン: this.state.dataはオブジェクトの配列です。これはフォーラムのトピックのリストを表し、お気に入りボタンはclickCollect()
を呼び出して切り替わります。状態に配列があるので、1つのアイテムのis_collectedプロパティを変更する場合、操作する配列のコピーを作成する必要があり、新しい値に変更した後、それを状態に設定できます。
_var data = this.state.data.slice(0); data[index].is_collected = !data[index].is_collected; this.setState({data: data});
_
_var data = this.state.data
_:これはポインタを配列にコピーし、Push()、shift()などが状態を直接変更します。 data
と_this.state.data
_の両方が影響を受けます。
var data = this.state.data.slice(0)
:これは浅いクローンを作成します。プッシュアンドシフトは状態を変更しませんが、私のクローンにはまだ状態の配列の要素へのポインターがあります。したがって、_data[0].is_collected
_を変更すると、_this.state.data[0].is_collected
_も変更されます。これは、setState()
を呼び出す前に発生します。
通常私はすべきです:
_var data = []; for (var i in this.state.data) { data.Push(this.state.data[i]); }
_
次に、インデックスの値を変更し、falseの場合はtrueに、trueの場合はfalseに設定します。
_data[index].is_collected = !data[index].is_collected;
_
そして状態を変更します:
_this.setState({data: data});
_
私の配列が比較的大きいか、非常に大きいと考えると、この反復によりAPPのパフォーマンスが低下すると思います。それが何らかの理由で正しい方法であることを知っていれば、私はその費用を支払います。ただし、この関数(clickCollect
)では常に新しい値を状態に設定しているため、変更を中止するという誤ったAPI応答を待機していません。すべての場合において、新しい値は状態に入ります。実際には、UIを再度レンダリングする場合にのみsetState
を呼び出します。だから質問は:
for var i in ...
_).slice(0)
)を作成することは理にかなっていますか?変更は配列内のオブジェクトに対して行われているので、コピー(_data = this.state.data
_)が行うのと同じように、浅いクローンはまだ私の状態を変更します。私のコードは単純化され、API呼び出しは簡略化のために省略されています。
これは初心者の質問なので、まったく異なるアプローチも歓迎します。または、他のQ&Aへのリンク。
_import React from 'react'; var ForumList = React.createClass({ render: function() { return <div className="section-inner"> {this.state.data.map(this.eachBox)} </div> }, eachBox: function(box, i) { return <div key={i} className="box-door"> <div className={"favorite " + (box.is_collected ? "on" : "off")} onTouchStart={this.clickCollect.bind(null, i)}> {box.id} </div> </div> }, getInitialState: function() { return {data: [ { id: 47, is_collected: false }, { id: 23, is_collected: false }, { id: 5, is_collected: true } ]}; }, clickCollect: function(index) { var data = this.state.data.slice(0); data[index].is_collected = !data[index].is_collected; this.setState({data: data}); } }); module.exports = ForumList;
_
個人的に私はいつもルールに従うわけではありません。あなたが何をしようとしているのかを本当に理解していれば、それは問題ではないと思います。
var data = this.state.data.slice(0);
data[index].is_collected = !data[index].is_collected;
this.setState({data: data});
この場合、状態を変更し、このようにsetState
を再度呼び出すと問題ありません。
this.state.data[index].is_collected = !this.state.data[index].is_collected;
this.setState({data: this.state.data});
状態の変更を避けるべき理由は、this.state.data
への参照があり、setState
を複数回呼び出すと、データが失われる可能性があるためです。
const myData = this.state.data
myData[0] = 'foo'
this.setState({ data: myData })
// do something...
// ...
const someNewData = someFunc()
this.setState({ data: someNewData })
myData[1] = 'bar' // myData is still referencing to the old state
this.setState({ data: myData }) // you lose everything of `someNewData`
これについて本当に心配している場合は、 immutable.js
反応のベストプラクティスに従う場合は、プロパティを変更するときに、すべての配列の浅いコピーを実行する必要があります。 「不変」ライブラリの実装を調べてください。
しかし、私の経験と私の意見から、 "shouldCompomenentUpdate"実装がある場合はsetState
メソッドを呼び出す必要があります。あなたの浅いコピーがはるかに多くのリソースを消費すると思い、仮想domチェックに反応する場合、これを行うことができます:
this.state.data[0].property = !this.state.data[0].property;
this.forceUpdate();
状態をミュートすると、Reactのデータフロー(一方向にされる)の主要な原則が直接破られ、アプリが非常に壊れやすくなり、基本的にコンポーネントのライフサイクル全体が無視されます。
そのため、setState({})を使用せずにコンポーネントの状態を変更することを妨げるものは何もありませんが、Reactを実際に利用したい場合は、それを絶対に回避する必要があります。
私があなたの質問を正しく理解していれば、オブジェクトの配列があり、配列内の単一のオブジェクトのプロパティが変化すると、
- 配列のディープクローンを作成し、setStateに渡します
- 浅いクローンを作成し、setStateに渡します
redux
sample todo app で確認したところ、オブジェクトの単一のプロパティが変更された場合は、配列全体ではなく、その単一のオブジェクトの新しいコピーを作成する必要があります。 redux
について読み、可能であればアプリの状態を管理することをお勧めします。