web-dev-qa-db-ja.com

反応状態で配列内のオブジェクトを置換

この質問は、「ベストプラクティス」の質問の側面に少し当てはまるかもしれませんが、ご容赦ください。

ここに私の状態の一部があります:

_this.state = {
  typeElements: {
    headers: [
        {
          name: "h1",
          size: 70,
          lineHeight: 1.25,
          kearning: 0,
          marginAfter: 0
        }, {
          name: "h2",
          size: 70,
          lineHeight: 1.25,
          kearning: 0,
          marginAfter: 0
        }, {
          name: "h3",
          size: 70,
          lineHeight: 1.25,
          kearning: 0,
          marginAfter: 0
        }...
_

私がする必要があるのは、ヘッダー配列の指定されたインデックスでオブジェクトを置き換えることです。

this.setState(headers[1] = {obj})のようにsetStateメソッドを使用してこれを行う方法はわかりませんが、明らかに無効です。私の現在の方法は、新しい配列を作成し、次のように古い配列を破壊しています。

__updateStyle(props) {
  let newState = Object.assign({}, this.state)
  newState.typeElements.headers[props.index] = props
  this.setState(newState)
};
_

私の小さなハックのプロジェクトでは大丈夫だと思いますが、これは非常に重い手で、あらゆる規模のパフォーマンスの問題にすぐにつながると感じています。

10
motleydev

更新:この回答はまだ賛成票を獲得しているため、以下の以前の回答は最新のJavaScriptとReactでは時代遅れになっていることに注意してください。 「更新」アドオンはレガシーになり、代わりに "immutability-helper" を使用できます。

React docsも理由を述べています 不変性が重要です 状態の変更を避けます。不変の更新には、Object.assign()または スプレッド構文 これは、ネストされたheadersオブジェクトとその配列要素のように、ネストのすべてのレベルで実行する必要があります。この特定の例では、配列インデックスをキーとして使用できるため、スプレッド演算子を使用して、配列の浅いクローンを作成し、クローン化された配列の指定されたインデックスの値として新しいオブジェクトを割り当てます。

_updateStyle (props) {
  const { typeElements } = this.state;
  const updatedHeaders = [...typeElements.headers];
  updatedHeaders[props.index] = props;
  this.setState({
    ...this.state,
    typeElements: {
      ...typeElements,
      headers: updatedHeaders
    }
  ));
}

Spread構文を必要とせず、置換するオブジェクトを見つけるために配列インデックスを使用しない場合に必要な別のソリューションは、array.mapを使用して新しい配列を作成し、代わりに新しいオブジェクトを返すことです。指定されたインデックスの古いもの。

  const updatedHeaders = typeElements.headers.map((obj, index) => {
    return index === props.index ? props : obj;
  });

Reduxドキュメントの同様の例でも "不変の更新パターン" を説明しています。

Reactには、このための不変性ヘルパーがいくつかあります。これについては、ドキュメントで説明しています: https://facebook.github.io/react/docs/update.html

あなたの場合、$ spliceコマンドを使用して、1つのアイテムを削除し、指定されたインデックスに新しいアイテムを追加できます。次に例を示します。

_updateStyle (props) {
  this.setState(update(this.state.typeElements, 
    { $splice: [[props.index, 1, props]] }
  ));
}
13
Robin Venneman

immutability-helper を使用します

素敵な例を見つけることができます

2
ChaosPredictor

これを達成する方法のより良い説明を提供します。

  • まず、状態配列で置換する要素のインデックスを見つけます。
  • 次に、updateはそのインデックスの要素
  • 第三に、setStateを新しいコレクションで呼び出します
import update from 'immutability-helper';

// this.state = { employees: [{id: 1, name: 'Obama'}, {id: 2, name: 'Trump'}] } 

updateEmployee(employee) {
    const index = this.state.employees.findIndex((emp) => emp.id === employee.id);
    const updatedEmployees = update(this.state.employees, {$splice: [[index, 1, employee]]});  // array.splice(start, deleteCount, item1)
    this.setState({employees: updatedEmployees});
}
0
daino3

私はあなたのアイデアを得た。私が知る限り、setState()メソッドは配列の子コンテンツを変更できません。そのため、代わりにそのメソッドを使用し、直接変更するだけです。

this.state.typeElements.headers[theIndex] = value;

ただし、直接変更すると、コンポーネントは再レンダリングされません。このメソッドで強制的に再レン​​ダリングできます。

this.forceUpdate()

注:ただし、このプラクティスは悪いと見なされるため、注意してください。状態が不変であることを確認する必要があります。ここ 不変配列参照

0

Object.Assignは、ディープコピーではなくシャローコピーを使用します。

あなたの例では、Object.assign({}, this.state)stateの最も近い子、つまりtypeElementsへのリンクのみをコピーしますが、headers配列はコピーされないことに注意してください。

構文上の砂糖... ES6のObject.Assignでは、Babelには特別なアドオン transform-object-rest-spread があります。

let newHeaders = { ...this.state.typeElements.headers};
newHeaders[index] = props.Something;

let newState = {...state, typeElements: { newHeaders }};
0
Artru