ReactでsetStateを使用してオブジェクトを更新する


265

オブジェクトのプロパティを更新することはまったく可能setStateですか?

何かのようなもの:

this.state = {
   jasper: { name: 'jasper', age: 28 },
}

私が試してみました:

this.setState({jasper.name: 'someOtherName'});

この:

this.setState({jasper: {name: 'someothername'}})

1つ目は構文エラーとなり、2つ目は何もしません。何か案は?


5
2番目のコードは機能しますが、age内部のプロパティは失われjasperます。
giorgim

回答:


577

状態更新があるので、これを行うための複数の方法がありますが、非同期操作ので、状態オブジェクトを更新するために、私たちが使用する必要があり、アップデータ機能をsetState

1-最も単純なもの:

最初にのコピーを作成してから、jasperその変更を行います。

this.setState(prevState => {
  let jasper = Object.assign({}, prevState.jasper);  // creating copy of state variable jasper
  jasper.name = 'someothername';                     // update the name property, assign a new value                 
  return { jasper };                                 // return new object jasper object
})

使用Object.assignする代わりに、次のように書くこともできます:

let jasper = { ...prevState.jasper };

2- スプレッド演算子の使用

this.setState(prevState => ({
    jasper: {                   // object that we want to update
        ...prevState.jasper,    // keep all other key-value pairs
        name: 'something'       // update the value of specific key
    }
}))

注: Object.assignおよびSpread Operator浅いコピーのみを作成するため、ネストされたオブジェクトまたはオブジェクトの配列を定義している場合は、別のアプローチが必要です。


ネストされた状態オブジェクトの更新:

状態を次のように定義したとします。

this.state = {
  food: {
    sandwich: {
      capsicum: true,
      crackers: true,
      mayonnaise: true
    },
    pizza: {
      jalapeno: true,
      extraCheese: false
    }
  }
}

ピザオブジェクトのextraCheeseを更新するには:

this.setState(prevState => ({
  food: {
    ...prevState.food,           // copy all other key-value pairs of food object
    pizza: {                     // specific object of food object
      ...prevState.food.pizza,   // copy all pizza key-value pairs
      extraCheese: true          // update value of specific key
    }
  }
}))

オブジェクトの配列を更新しています:

todoアプリがあり、次の形式でデータを管理しているとします。

this.state = {
  todoItems: [
    {
      name: 'Learn React Basics',
      status: 'pending'
    }, {
      name: 'Check Codebase',
      status: 'pending'
    }
  ]
}

todoオブジェクトのステータスを更新するには、配列に対してマップを実行し、各オブジェクトの一意の値を確認しcondition=trueます。の場合、更新された値を持つ新しいオブジェクトを返します。それ以外は同じオブジェクトです。

let key = 2;
this.setState(prevState => ({

  todoItems: prevState.todoItems.map(
    el => el.key === key? { ...el, status: 'done' }: el
  )

}))

提案:オブジェクトに一意の値がない場合は、配列インデックスを使用してください。


私が試していた方法は今でも機能します... 2番目の方法:S
JohnSnow

7
@JohnSnowの2番目に、jasperオブジェクトから他のプロパティが削除され、console.log(jasper)キーが1つだけ表示されますname。年齢は表示されません:)
Mayank Shukla

1
@JohnSnowは、はいこの方法は、以下の場合に適切でjasperあるobject、最善のかどうか、他のより良いソリューションが可能:)していることがありません
Mayankシュクラ

6
ここでは、Object.assignオペレータのディープコピープロパティも拡散もしないことを述べておくとよいでしょう。この方法では、lodashのような回避策使用する必要がありますdeepCopyなど
アレックスVasilevの

1
どのように試合let { jasper } = this.state
ブライアンル

37

これが最も速く、最も読みやすい方法です。

this.setState({...this.state.jasper, name: 'someothername'});

this.state.jasperすでに名前プロパティが含まれている場合でも、新しい名前name: 'someothername'が使用されます。


38
このソリューションには大きな欠点が1つあります。状態の更新を最適化するために、Reactは複数の更新をグループ化する場合があります。結果として、this.state.jasper必ずしも最新の状態が含まれているわけではありません。の表記法を使用することをお勧めしますprevState
罪を犯した

33

ここでスプレッド演算子といくつかのES6を使用します

this.setState({
    jasper: {
          ...this.state.jasper,
          name: 'something'
    }
})

これによりジャスパーが更新されますが、他のプロパティは状態から削除されます。
iaq

11

このソリューションを使用しました。

次のようなネストされた状態がある場合:

this.state = {
  formInputs:{
    friendName:{
      value:'',
      isValid:false,
      errorMsg:''
    },
    friendEmail:{
      value:'',
      isValid:false,
      errorMsg:''
    }
  }
}

現在のステータスをコピーして変更された値で再割り当てするhandleChange関数を宣言できます

handleChange(el) {
    let inputName = el.target.name;
    let inputValue = el.target.value;

    let statusCopy = Object.assign({}, this.state);
    statusCopy.formInputs[inputName].value = inputValue;

    this.setState(statusCopy);
  }

ここでは、イベントリスナーを含むhtmlです。状態オブジェクトに使用されているのと同じ名前を使用してください(この場合は 'friendName')

<input type="text" onChange={this.handleChange} " name="friendName" />

私が代わりにこれを使用していた以外これは、私の仕事:statusCopy.formInputs[inputName] = inputValue;
MikeyE

9

ここにはたくさんの答えがあることはわかっていますが、setStateの外に新しいオブジェクトのコピーを作成し、その後単にsetState({newObject})を作成しないことに驚いています。クリーンで簡潔、信頼できる。したがって、この場合:

const jasper = { ...this.state.jasper, name: 'someothername' }
this.setState(() => ({ jasper }))

または動的プロパティの場合(フォームに非常に役立ちます)

const jasper = { ...this.state.jasper, [VarRepresentingPropertyName]: 'new value' }
this.setState(() => ({ jasper }))


7

これを試してください、それはうまくいくはずです

this.setState(Object.assign(this.state.jasper,{name:'someOtherName'}));

4
状態オブジェクトを直接変更しています。このアプローチを使用するには、ソースとして新しいオブジェクトを追加しますObject.assign({}, this.state.jasper, {name:'someOtherName'})
アルビジア

3

最初のケースは確かに構文エラーです。

コンポーネントの残りの部分は見えないので、ここでオブジェクトを自分の状態でネストしている理由を理解するのは困難です。オブジェクトをコンポーネント状態でネストすることはお勧めできません。初期状態を次のように設定してみてください:

this.state = {
  name: 'jasper',
  age: 28
}

そうすれば、名前を更新したい場合は、次のように呼び出すだけです。

this.setState({
  name: 'Sean'
});

それはあなたが目指していることを達成しますか?

大規模で複雑なデータストアの場合は、Reduxなどを使用します。しかし、それははるかに高度です。

コンポーネントの状態に関する一般的な規則は、コンポーネントのUI状態(アクティブ、タイマーなど)を管理するためだけに使用することです。

これらの参照をチェックしてください:


1
私はそれを例として使用しただけで、オブジェクトは入れ子にする必要があります。おそらくReduxを使用する必要がありますが、Reactの
ファンダメンタルズ

2
ええ、私は今のところReduxから離れています。基本を学びながら、データをシンプルに保つようにしてください。それはあなたがあなたをつまずく奇妙なケースを避けるのに役立ちます。👍
mccambridge

2

別のオプション:Jasperオブジェクトから変数を定義して、変数を呼び出すだけです。

スプレッドオペレーター:ES6

this.state = {  jasper: { name: 'jasper', age: 28 } } 

let foo = "something that needs to be saved into state" 

this.setState(prevState => ({
    jasper: {
        ...jasper.entity,
        foo
    }
})

2

シンプルでダイナミックな方法。

これでうまくいきますが、親がすべてのIDを親に設定して、親がオブジェクトの名前を指すようにする必要があります。つまり、id = "jasper"で、オブジェクトジャスパー内の入力要素の名前=プロパティに名前を付けます。 。

handleChangeObj = ({target: { id , name , value}}) => this.setState({ [id]: { ...this.state[id] , [name]: value } });

最初の行に同意します。これは、状態にあるオブジェクトのオブジェクトに対して機能した唯一のものであるためです。また、データの更新で行き詰まっている場所もクリアされました。これまでで最もシンプルでダイナミックな方法...ありがとう!
スミット

2

これで試すことができます:(:入力タグの名前===オブジェクトのフィールド)

<input name="myField" type="text" 
      value={this.state.myObject.myField} 
     onChange={this.handleChangeInpForm}>
</input>

-----------------------------------------------------------
handleChangeInpForm = (e) => {
   let newObject = this.state.myObject;
   newObject[e.target.name] = e.target.value;
   this.setState({
     myObject: newObject 
   })
}

Stack Overflowへようこそ。このコードは質問に答えることがありますが、このコードが質問に答える理由や方法に関する追加のコンテキストを提供すると、長期的な価値が向上します。回答方法
Elletlar

2

これはimmer immutabeユーティリティを使用した別のソリューションであり、深くネストされたオブジェクトに非常に適しているため、突然変異を気にする必要はありません。

this.setState(
    produce(draft => {
       draft.jasper.name = 'someothername
    })
)

1

また、すべての「状態」オブジェクトをコピーしたくない場合は、Alberto Pirasソリューションに従ってください。

handleChange(el) {
    let inputName = el.target.name;
    let inputValue = el.target.value;

    let jasperCopy = Object.assign({}, this.state.jasper);
    jasperCopy[inputName].name = inputValue;

    this.setState({jasper: jasperCopy});
  }

1

これで試すことができます:

this.setState(prevState => {
   prevState = JSON.parse(JSON.stringify(this.state.jasper));
   prevState.name = 'someOtherName';
   return {jasper: prevState}
})

または他のプロパティ:

this.setState(prevState => {
   prevState = JSON.parse(JSON.stringify(this.state.jasper));
   prevState.age = 'someOtherAge';
   return {jasper: prevState}
})

または、handleChage関数を使用できます。

handleChage(event) {
   const {name, value} = event.target;
    this.setState(prevState => {
       prevState = JSON.parse(JSON.stringify(this.state.jasper));
       prevState[name] = value;
       return {jasper: prevState}
    })
}

およびHTMLコード:

<input 
   type={"text"} 
   name={"name"} 
   value={this.state.jasper.name} 
   onChange={this.handleChange}
/>
<br/>
<input 
   type={"text"} 
   name={"age"} 
   value={this.state.jasper.age} 
   onChange={this.handleChange}
/>

これはJSON.stringifyなしで機能します.. this.setState(prevState => {prevState = this.state.document; prevState.ValidDate = event.target.value; prevState.ValidDateFormat = dayFormat; return {document:prevState}}); ..Where document is state type object ..
Oleg Borodko

1

非同期を使用せずに、これを使用する...

funCall(){    
     this.setState({...this.state.jasper, name: 'someothername'});
}

Async And Awaitを使用している場合は、これを使用してください...

async funCall(){
      await this.setState({...this.state.jasper, name: 'someothername'});
}

0

この設定は私にとってはうまくいきました:

let newState = this.state.jasper;
newState.name = 'someOtherName';

this.setState({newState: newState});

console.log(this.state.jasper.name); //someOtherName
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.