Cập nhật một đối tượng với setState trong React


265

Có khả năng cập nhật các thuộc tính của đối tượng setStatekhông?

Cái gì đó như:

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

Tôi đã thử:

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

và điều này:

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

Kết quả đầu tiên là lỗi cú pháp và lần thứ hai không làm gì cả. Có ý kiến ​​gì không?


5
mã thứ hai sẽ hoạt động tuy nhiên bạn sẽ mất agetài sản bên trong jasper.
giorgim

Câu trả lời:


577

Có nhiều cách để làm điều này, vì cập nhật trạng thái là một hoạt động không đồng bộ , vì vậy để cập nhật đối tượng trạng thái, chúng ta cần sử dụng chức năng cập nhật với setState.

1- Đơn giản nhất:

Đầu tiên tạo một bản sao jaspersau đó thực hiện các thay đổi trong đó:

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

Thay vì sử dụng Object.assignchúng ta cũng có thể viết nó như thế này:

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

2- Sử dụng toán tử trải :

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

Lưu ý: Object.assignSpread Operatorchỉ tạo bản sao nông , vì vậy nếu bạn đã xác định đối tượng lồng hoặc mảng đối tượng, bạn cần một cách tiếp cận khác.


Cập nhật đối tượng trạng thái lồng nhau:

Giả sử bạn đã xác định trạng thái là:

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

Để cập nhật thêmCheese của đối tượng pizza:

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

Cập nhật mảng các đối tượng:

Giả sử bạn có ứng dụng việc cần làm và bạn đang quản lý dữ liệu ở dạng này:

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

Để cập nhật trạng thái của bất kỳ đối tượng việc cần làm nào, hãy chạy bản đồ trên mảng và kiểm tra một số giá trị duy nhất của từng đối tượng, trong trường hợp condition=true, trả về đối tượng mới có giá trị được cập nhật, cùng một đối tượng.

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

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

}))

Gợi ý: Nếu đối tượng không có giá trị duy nhất, thì hãy sử dụng chỉ mục mảng.


Cách mà tôi đã thử bây giờ hoạt động mặc dù ... cách thứ hai: S
JohnSnow

7
@JohnSnow trong bạn thứ hai nó sẽ xóa các thuộc tính khác khỏi jasperđối tượng, làm console.log(jasper)bạn sẽ chỉ thấy một khóa name, tuổi sẽ không ở đó :)
Mayank Shukla

1
@ JohnSnow, vâng, cách này là phù hợp nếu jasperlà một object, không nên dù tốt nhất hay không, có thể là giải pháp tốt hơn khác có thể :)
Mayank Shukla

6
Tốt để đề cập ở đây rằng không phải Object.assigncũng không lan truyền các thuộc tính sao chép sâu. Theo cách này, bạn nên sử dụng cách giải quyết như lodash, deepCopyv.v.
Alex Vasilev

1
Cơn sốt thế nào let { jasper } = this.state?
Brian Lê

37

Đây là cách nhanh nhất và dễ đọc nhất:

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

Ngay cả khi this.state.jasperđã có một thuộc tính tên, tên mới name: 'someothername'được sử dụng.


38
Giải pháp này có một nhược điểm lớn: để tối ưu hóa các cập nhật trạng thái React có thể nhóm nhiều cập nhật. Kết quả là this.state.jasperkhông nhất thiết phải chứa trạng thái mới nhất. Sử dụng tốt hơn các ký hiệu với prevState.
phạm tội

33

Sử dụng toán tử trải rộng và một số ES6 tại đây

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

Bản cập nhật này jasper nhưng các thuộc tính khác được xóa khỏi trạng thái.
iaq

11

Tôi đã sử dụng giải pháp này.

Nếu bạn có một trạng thái lồng nhau như thế này:

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

bạn có thể khai báo hàm handleChange sao chép trạng thái hiện tại và gán lại nó với các giá trị đã thay đổi

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

ở đây html với người nghe sự kiện. Đảm bảo sử dụng cùng tên được sử dụng vào đối tượng trạng thái (trong trường hợp này là 'friendName')

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

Điều này làm việc cho tôi, ngoại trừ tôi phải sử dụng điều này thay vào đó:statusCopy.formInputs[inputName] = inputValue;
MikeyE

9

Tôi biết có rất nhiều câu trả lời ở đây, nhưng tôi ngạc nhiên không ai trong số họ tạo ra một bản sao của đối tượng mới bên ngoài setState, và sau đó chỉ đơn giản là setState ({newObject}). Sạch sẽ, súc tích và đáng tin cậy. Vì vậy, trong trường hợp này:

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

Hoặc cho một thuộc tính động (rất hữu ích cho các biểu mẫu)

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


7

Hãy thử nó, nó sẽ hoạt động tốt

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

4
Bạn đang đột biến đối tượng nhà nước trực tiếp. Để sử dụng phương pháp này, hãy thêm một đối tượng mới làm nguồn:Object.assign({}, this.state.jasper, {name:'someOtherName'})
Albizia

3

Trường hợp đầu tiên thực sự là một lỗi cú pháp.

Vì tôi không thể thấy phần còn lại của thành phần của bạn, thật khó để biết lý do tại sao bạn làm tổ các đối tượng ở trạng thái của bạn ở đây. Nó không phải là một ý tưởng tốt để lồng các đối tượng trong trạng thái thành phần. Hãy thử đặt trạng thái ban đầu của bạn là:

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

Bằng cách đó, nếu bạn muốn cập nhật tên, bạn chỉ cần gọi:

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

Điều đó sẽ đạt được những gì bạn đang hướng tới?

Đối với các cửa hàng dữ liệu lớn hơn, phức tạp hơn, tôi sẽ sử dụng một cái gì đó như Redux. Nhưng đó là cao cấp hơn nhiều.

Nguyên tắc chung với trạng thái thành phần là chỉ sử dụng nó để quản lý trạng thái UI của thành phần (ví dụ: hoạt động, bộ hẹn giờ, v.v.)

Kiểm tra các tài liệu tham khảo sau:


1
Tôi chỉ sử dụng nó như một ví dụ, đối tượng phải được lồng vào nhau .. Có lẽ tôi nên sử dụng Redux nhưng tôi đang cố gắng hiểu các phản ứng cơ bản ..
JohnSnow

2
Vâng, tôi sẽ tránh xa Redux bây giờ. Trong khi bạn đang học các nguyên tắc cơ bản, hãy cố gắng giữ cho dữ liệu của bạn đơn giản. Điều đó sẽ giúp bạn tránh những trường hợp kỳ lạ làm bạn vấp ngã. 👍
McCambridge

2

Một tùy chọn khác: xác định biến của bạn ra khỏi đối tượng Jasper và sau đó chỉ cần gọi một biến.

Toán tử trải rộng: 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

Cách đơn giản và năng động.

Điều này sẽ thực hiện công việc, nhưng bạn cần đặt tất cả các id thành cha mẹ để cha mẹ sẽ trỏ đến tên của đối tượng, là id = "jasper" và đặt tên của phần tử đầu vào = thuộc tính bên trong đối tượng jasper .

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

Tôi đồng ý với dòng đầu tiên, vì đây là điều duy nhất hoạt động cho đối tượng của tôi trong các đối tượng ở trạng thái! Ngoài ra, nó đã xóa nơi tôi bị mắc kẹt trong việc cập nhật dữ liệu của tôi. Cho đến nay, cách đơn giản và năng động nhất ... Cảm ơn !!
Smit

2

Bạn có thể thử với điều này : ( Lưu ý : tên của thẻ đầu vào === trường của đối tượng)

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

Chào mừng bạn đến với Stack Overflow. Mặc dù mã này có thể trả lời câu hỏi, cung cấp ngữ cảnh bổ sung về lý do và / hoặc cách mã này trả lời câu hỏi cải thiện giá trị lâu dài của nó. Cách trả lời
Elletlar

2

đây là một giải pháp khác sử dụng tiện ích imutabe immer , rất phù hợp cho các đối tượng được lồng sâu một cách dễ dàng và bạn không nên quan tâm đến đột biến

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

1

Ngoài ra, theo giải pháp của Alberto Piras, nếu bạn không muốn sao chép tất cả các đối tượng "trạng thái":

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

Bạn có thể thử với điều này:

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

hoặc cho tài sản khác:

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

Hoặc bạn có thể sử dụng chức năng 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}
    })
}

và mã 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}
/>

Điều này hoạt động mà không có JSON.opesify .. this.setState (trước .. Tài liệu ở đâu là đối tượng loại trạng thái ..
Oleg Borodko

1

Không sử dụng Async và chờ đợi Sử dụng ...

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

Nếu bạn sử dụng với Async và đang chờ, hãy sử dụng ...

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

0

Thiết lập này làm việc cho tôi:

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

this.setState({newState: newState});

console.log(this.state.jasper.name); //someOtherName
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.