วิธีที่ต้องการในการกลายพันธุ์สถานะปฏิกิริยาคืออะไร?


97

สมมติว่าฉันมีรายการวัตถุธรรมดาอยู่ในthis.state.listนั้นฉันสามารถใช้เพื่อแสดงรายการเด็กได้ แล้ววิธีที่ถูกต้องในการแทรกวัตถุthis.state.listคืออะไร?

ด้านล่างนี้เป็นวิธีเดียวที่ฉันคิดว่ามันใช้ได้เพราะคุณไม่สามารถกลายพันธุ์this.stateได้โดยตรงตามที่กล่าวไว้ในเอกสาร

this._list.push(newObject):
this.setState({list: this._list});

นี่ดูน่าเกลียดสำหรับฉัน มีวิธีที่ดีกว่า?



คำตอบทั้งหมดนั้นเกี่ยวกับการเพิ่มองค์ประกอบในอาร์เรย์ แต่สิ่งที่เกี่ยวกับการลบองค์ประกอบออกจากมัน? หรือเพียงแค่รีเซ็ตอาร์เรย์เป็นอาร์เรย์ใหม่ทั้งหมด?
Augustin Riedinger

คำตอบ:


164

concat ส่งคืนอาร์เรย์ใหม่ดังนั้นคุณสามารถทำได้

this.setState({list: this.state.list.concat([newObject])});

อีกทางเลือกหนึ่งคือตัวช่วยความไม่เปลี่ยนรูปของ React

  var newState = React.addons.update(this.state, {
      list : {
        $push : [newObject]
      }
  });

  this.setState(newState);

6
concatthis.state.list.concat([newObject])ใช้เวลาอาร์เรย์ดังนั้นคุณจะต้องการ
Sophie Alpert

@BenAlpert ทำงานให้ฉันใน Chrome คุณกำลังบอกว่านี่เป็นพฤติกรรมที่ไม่ได้มาตรฐาน
Heap

6
@Heap อืมถ้าคุณส่งอาร์เรย์ที่ไม่ใช่อาร์เรย์ไปconcatมันจะรวมอยู่ในอาร์เรย์ แต่จะเป็นการดีกว่าที่จะเพิ่มอาร์เรย์ด้วยตัวคุณเองให้ชัดเจน: ถ้าคุณมี[[1,2], [3,4]]และต้องการเพิ่ม[5,6]เป็นองค์ประกอบถัดไปคุณต้องทำ.concat([[5,6]])อย่างอื่น จะลงเอยด้วย[[1,2], [3,4], 5, 6].
Sophie Alpert

2
เท่าที่ฉันชื่นชมความคิดเบื้องหลังตัวช่วยที่ไม่เปลี่ยนรูป (และฉันอาจจะใช้มันต่อไป) ขอArray.concatให้เต้นเพิ่มไลบรารีอื่น
Shawn Erquhart

1
การเรียกใช้ this.setState ด้วยค่าที่ได้มาจาก this.state จะทำให้คุณไม่สามารถแก้ไขปัญหาการจัดชุดการอัปเดตได้ ดูstackoverflow.com/a/41445812/1998186ซึ่งลิงก์ไปยัง jsfiddle ที่แสดงปัญหาที่คุณจะได้รับ
NealeU

40

setState ()สามารถเรียกได้โดยใช้ฟังก์ชันเป็นพารามิเตอร์:

this.setState((state) => ({ list: state.list.concat(newObj) }))

หรือใน ES5:

this.setState(function(state) {
  return {
   list: state.list.concat(newObj)
  }
})

2
@Arian: React จะดำเนินการตามคำสั่งการกลายพันธุ์ DOM ที่จำเป็นซึ่งจำเป็นในการแสดงผลเฉพาะองค์ประกอบที่เพิ่มเข้ามาใหม่ แน่นอนว่าองค์ประกอบที่คุณเพิ่มเข้ามาใหม่จะไม่เปลี่ยนวิธีการแสดงผลขององค์ประกอบก่อนหน้านี้
Jose Browne

1
นี่เป็นคำตอบที่ยอดเยี่ยมและให้ความรู้สึกสะอาดกว่าการต่ออาร์เรย์เพียงเพื่อทำการพุชแม้ว่าจะเป็นความคิดเห็นที่ฉันเดา
Matt Styles

2
@Nyalab คุณไม่ควรใส่ฟังก์ชันไว้ในวงเล็บใช่หรือไม่? ตามที่แสดงในตัวอย่างของคุณวงเล็บปีกกาหลังลูกศรอ้วนจะเริ่มบล็อกไม่ใช่วัตถุ สิ่งนี้:this.setState((state) => ({ list: state.list.push(newObj) }))
kumarharsh

3
รหัสนี้ทำงานอย่างไร? วิธีการกดจะส่งคืน a NUMBERซึ่งจะถูกตั้งค่าในlistตัวแปร
kumarharsh

1
คุณยังสามารถใช้ splat:this.setState((state) => ({ list: [...state.list, newObj] }))
amoebe

19

อัปเดต 2016

ด้วย ES6 คุณสามารถใช้:

this.setState({ list: [...this.state.list, ...newObject] });

5
...[newObject]ก็เหมือนกับ just newObject.
amoebe

2
วิธีนี้ใช้ไม่ได้กับการอัปเดตเป็นกลุ่ม ดูตัวอย่างของฉันที่stackoverflow.com/a/41445812/1998186
NealeU

7

จากเอกสารตอบกลับ ( https://facebook.github.io/react/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous ):

เนื่องจาก this.props และ this.state อาจได้รับการอัปเดตแบบอะซิงโครนัสคุณจึงไม่ควรใช้ค่าเหล่านี้ในการคำนวณสถานะถัดไป

ดังนั้นคุณควรทำสิ่งนี้แทน:

this.setState((prevState) => ({
  contacts: prevState.contacts.concat([contact])
}));
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.