在React中使用setState更新对象


265

是否有可能用更新对象的属性setState

就像是:

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

我努力了:

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

还有这个:

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

第一个导致语法错误,第二个则什么都不做。有任何想法吗?


5
第二个代码本来可以工作,但是您会丢失其中的age属性jasper
giorgim

Answers:


576

有多种方法可以执行此操作,因为状态更新是异步操作,因此要更新状态对象,我们需要使用带有updater函数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
    }
  }
}))

更新对象数组:

假设您有一个待办事项应用程序,并且您正在以这种形式管理数据:

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

要更新任何待办事项对象的状态,请在数组上运行一个映射并检查每个对象的某些唯一值,如果为condition=true,则返回具有更新值的新对象,否则返回相同的对象。

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

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

}))

建议:如果对象没有唯一值,则使用数组索引。


我尝试的方式现在可以使用...第二种方式:S
JohnSnow

7
@JohnSnow在您第二次将从jasper对象中删除其他属性,这样做console.log(jasper)您只会看到一个键name,年龄将不存在:)
Mayank Shukla

1
@JohnSnow,是jasperobject,如果是,这种方法是正确的,而不管是否最好,可能还有其他更好的解决方案:)
Mayank Shukla

6
值得一提的是,既不Object.assign传播运算符,也不传播深度复制属性。通过这种方式,你应该使用像lodash解决方法deepCopy
亚历克斯·瓦西列夫

1
怎么样let { jasper } = this.state
布赖恩·勒

37

这是最快,最易读的方式:

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

即使this.state.jasper已经包含name属性,name: 'someothername'也要使用新名称。


38
这个解决方案有一个很大的缺点:为了优化状态更新,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'})
Albizia

3

第一种情况确实是语法错误。

由于看不到组件的其余部分,因此很难在这里看到为什么将对象嵌套在状态中。将对象嵌套在组件状态不是一个好主意。尝试将您的初始状态设置为:

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

这样,如果您想更新名称,则可以调用:

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

这样可以实现您的目标吗?

对于更大,更复杂的数据存储,我将使用Redux之类的东西。但这要先进得多。

具有组件状态的一般规则是仅使用它来管理组件的UI状态(例如活动状态,计时器等)。

查看以下参考:


1
我仅以该示例为例,该对象必须嵌套。.我可能应该使用Redux,但是我试图理解React
Fundementals。– JohnSnow

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”,并在对象jasper内将输入元素的名称=属性命名。

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

我同意第一行,因为这是唯一对状态对象有效的东西!同时也清除了我在更新数据时遇到的困难。到目前为止,最简单,最动态的方式...谢谢!
Smit

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 
   })
}

欢迎使用堆栈溢出。尽管此代码可以回答问题,但提供有关此代码为何和/或如何回答问题的其他上下文,可以提高其长期价值。如何回答
Elletlar '19

2

这是用另一种解决办法音麦 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文档状态类型对象..
奥列格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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.