ChatGPT解决这个技术问题 Extra ChatGPT

在 React 中使用 setState 更新对象

是否可以使用 setState 更新对象的属性?

就像是:

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

我努力了:

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

和这个:

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

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

第二个代码会起作用,但是您会丢失 jasper 中的 age 属性。
我知道 React 使用 .assign() 来。将旧状态对象与新对象合并,所以第二个代码不应该正常工作吗?

M
Mayank Shukla

有多种方法可以做到这一点,因为状态更新是 async operation,所以要更新状态对象,我们需要使用 updater functionsetState

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- 使用 spread syntax

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.assignSpread Operator 只创建 shallow copy,因此如果您定义了嵌套对象或对象数组,则需要不同的方法。

更新嵌套状态对象:

假设您已将状态定义为:

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'
    }
  ]
}

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

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

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

}))

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


@JohnSnow 在您第二次中,它将从 jasper 对象中删除其他属性,执行 console.log(jasper) 您将只看到一个键 name,年龄将不存在 :)
@JohnSnow,是的,如果 jasperobject,这种方式是正确的,不管是否最好,可能还有其他更好的解决方案:)
值得一提的是,Object.assign 和传播操作符深拷贝属性都没有。这样,您应该使用 lodash deepCopy 等变通方法。
let { jasper } = this.state怎么样?
@ankur_rajput 我们使用扩展运算符创建浅拷贝,您也可以使用 Object.assign。避免状态对象的直接突变。
T
Tim Gerhard

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

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

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


这个解决方案有一个很大的缺点:为了优化状态更新,React 可能会分组多个更新。因此,this.state.jasper 不一定包含最新状态。最好使用带有 prevState 的符号。
J
Just code

在这里使用扩展运算符和一些 ES6

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

这会更新 jasper,但会从状态中删除其他属性。
c
colemerrick

我知道这里有很多答案,但我很惊讶他们都没有在 setState 之外创建新对象的副本,然后只是 setState({newObject})。干净、简洁、可靠。所以在这种情况下:

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

或者对于动态属性(对表单非常有用)

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


因为您使用了两个语句,其中只需要一个。
r
ravibagul91

我使用了这个解决方案。

如果您有这样的嵌套状态:

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;
B
Burak Kahraman

试试这个,它应该可以正常工作

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

您正在直接改变状态对象。要使用这种方法,请添加一个新对象作为源:Object.assign({}, this.state.jasper, {name:'someOtherName'})
R
Ryan Dantzler

创建状态对象

this.state = {
  objName: {
    propertyOne: "",
    propertyTwo: ""
  }
};

使用 setState 更新状态

this.setState(prevState => ({
  objName: {
    ...prevState.objName,
    propertyOne: "Updated Value",
    propertyTwo: "Updated value"
  }
}));

m
mccambridge

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

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

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

这样,如果您想更新名称,您只需调用:

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

这会实现你的目标吗?

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

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

查看这些参考资料:

https://facebook.github.io/react/docs/react-component.html#state

https://facebook.github.io/react/docs/state-and-lifecycle.html


我只是用它作为例子,对象必须是嵌套的。我可能应该使用 Redux,但我试图理解 React 的基本原理。
是的,我现在会远离 Redux。在学习基础知识时,请尽量保持数据简单。这将帮助您避免绊倒您的奇怪情况。 👍
s
samehanwar

这是使用 immer immutabe 实用程序的另一种解决方案,非常适合轻松嵌套的对象,您不应该关心突变

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

h
himanshu sharma

如果更新键为字符串的对象

例如,假设您的状态对象是

serviceDays: {
    Sunday: true,
    Monday: true,
    Tuesday: false,
    Wednesday: true,
    Thurday: false,
    Friday: true,
    Saturday: true
  }

所以你可以通过以下方式更新

const onDayClick = day => {
  const { serviceDays } = this.state
  this.setState(prevState => ({
    serviceDays: {
      ...prevState.serviceDays,
      [day]: serviceDays[day] ? false : true
    }
  }))
}

R
Rajesh Nasit

使用钩子我们可以做以下方式

const [student, setStudent] = React.useState({name: 'jasper', age: 28});
 setStudent((prevState) => ({
          ...prevState,
          name: 'newName',
        }));

J
J. Steen

另一种选择:从 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
    }
})

N
Nemanja Stojanovic

你可以试试这个:

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} }); ..其中文档是状态类型对象..
A
Alejandro Sanchez Duran

简单而动态的方式。

这将完成这项工作,但您需要将所有 id 设置为父级,以便父级指向对象的名称,即 id = "jasper" 并命名输入元素的名称 = 对象内的属性 jasper .

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

我同意第一行,因为这是唯一对我的状态对象有效的东西!它还清除了我在更新数据时遇到的问题。到目前为止,最简单和最动态的方式......谢谢!
S
Shantanu Sharma

不使用 Async 和 Await 使用这个...

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

如果您与 Async And Await 一起使用,请使用此...

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

r
ravibagul91

你可以试试这个:(注意:输入标签的名称===对象的字段)

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

欢迎来到堆栈溢出。虽然此代码可能会回答问题,但提供有关此代码为何和/或如何回答问题的额外上下文可提高其长期价值。 How to Answer
A
Anurag Tripathi

在功能组件中使用钩子:

const [state, setState] = useState({jasper: { name: 'jasper', age: 28 }})
const nameChangeHandler = () => {
      setState(prevState => ({
            ...prevState,
            prevState.jasper.name = "Anurag",
            prevState.jasper.age = 28
      })
    )
}

在这些情况下,建议使用基于回调的方法来更新状态,因为使用这种方法可以确保先前的状态完全更新,并且我们正在根据先前更新的状态进行更新。


M
Marc LaQuay

此外,如果您不想复制所有“状态”对象,请遵循 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});
  }

D
David Jesus

试试这个:

const { jasper } = this.state; //Gets the object from state
jasper.name = 'A new name'; //do whatever you want with the object
this.setState({jasper}); //Replace the object in state

P
Peppe426

通过使用 input html 输入名称属性,我们可以采用更动态的方法来更新对象属性。

DOM html input name attribute

<input type="text" name="fname" handleChange={(e: any) => { updatePerson(e) }}/>
<input type="text" name="lname" handleChange={(e: any) => { updatePerson(e) }}/>

反应/TSX object.assign

const [person, setPerson] = useState<IPerson>({});

   function updatePerson(e: React.ChangeEvent<HTMLInputElement>): void {
        const { name, value } = e.currentTarget;

        setPerson(prevState => {
            const newState = Object.assign(person, { [name]: value })
            return { ...prevState, ...newState };
        });
    }

L
LaZza

此设置对我有用:

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

this.setState({newState: newState});

console.log(this.state.jasper.name); //someOtherName

n
nolan

您的第二种方法不起作用,因为 {name: 'someothername'} 等于 {name: 'someothername', age: undefined},因此 undefined 将覆盖原始年龄值。

当涉及更改嵌套对象的状态时,一个好的方法是 Immutable.js

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

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

S
Shahnad

示例 FC:

   const [formData, setformData] = useState({
           project_admin_permissions: {
              task_forms: false,
              auto_assign_rules: false,
              project_notes: true,
              alerts: false,
              update_criteria: true,
              project_flow: false,
              reports: false,
            }
        
          })
    
     const handleChangeCheckBox = (e) => {
       setformData({
          ...formData, project_admin_permissions: { ...formData.project_admin_permissions, [e.target.name]: e.target.checked }
    
        })
      }