【问题标题】:Updating an object with setState in React在 React 中使用 setState 更新对象
【发布时间】:2023-04-01 01:42:02
【问题描述】:

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

类似:

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

我试过了:

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

还有这个:

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

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

【问题讨论】:

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

标签: reactjs state


【解决方案1】:

有多种方法可以做到这一点,因为状态更新是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。避免状态对象的直接突变。
【解决方案2】:

这是最快最易读的方式:

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

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

【讨论】:

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

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

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

【讨论】:

  • 这会更新 jasper,但会从状态中删除其他属性。
【解决方案4】:

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

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

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

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

【讨论】:

  • 因为您使用了两个语句,而其中只需要一个。
【解决方案5】:

我使用了这个解决方案。

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

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;
【解决方案6】:

试试这个,应该可以的

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

【讨论】:

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

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

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

【讨论】:

    【解决方案8】:

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

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

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

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

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

    这会实现您的目标吗?

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

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

    查看这些参考资料:

    【讨论】:

    • 我只用这个作为例子,对象必须嵌套。我可能应该使用 Redux,但我正在尝试理解 React 的基本原理。
    • 是的,我现在会远离 Redux。在学习基础知识时,请尽量保持数据简单。这将帮助您避免绊倒您的奇怪情况。 ?
    【解决方案9】:

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

    【讨论】:

      【解决方案10】:

      你可以试试这个:

      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 {文档:prevState} }); ..其中文档是状态类型对象..
      【解决方案11】:

      简单而动态的方式。

      这将完成这项工作,但是您需要将所有 id 设置为父级,以便父级将指向对象的名称,即 id = "jasper" 并命名输入元素的名称 = property inside对象碧玉。

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

      【讨论】:

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

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

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

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

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

      【讨论】:

        【解决方案13】:

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

        <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。虽然此代码可能会回答问题,但提供有关此代码为何和/或如何回答问题的额外上下文可提高其长期价值。 How to Answer
        【解决方案14】:

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

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

        【讨论】:

          【解决方案15】:

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

          【讨论】:

            【解决方案16】:

            试试这个:

            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
            

            【讨论】:

              【解决方案17】:

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

              例如假设你的状态对象是

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

              【讨论】:

                【解决方案18】:

                通过使用 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 };
                        });
                    }
                

                【讨论】:

                  【解决方案19】:

                  这个设置对我有用:

                  let newState = this.state.jasper;
                  newState.name = 'someOtherName';
                  
                  this.setState({newState: newState});
                  
                  console.log(this.state.jasper.name); //someOtherName
                  

                  【讨论】:

                    【解决方案20】:

                    您的第二种方法不起作用,因为{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')})
                    

                    【讨论】:

                      【解决方案21】:

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

                      【讨论】:

                        猜你喜欢
                        • 2018-10-14
                        • 2020-07-23
                        • 2018-08-27
                        • 2018-06-26
                        • 1970-01-01
                        • 2021-11-11
                        • 2021-08-02
                        相关资源
                        最近更新 更多