การลบองค์ประกอบจากอาร์เรย์ในสถานะส่วนประกอบ


131

ฉันกำลังพยายามหาวิธีที่ดีที่สุดในการลบองค์ประกอบออกจากอาร์เรย์ในสถานะขององค์ประกอบ เนื่องจากฉันไม่ควรแก้ไขthis.stateตัวแปรโดยตรงมีวิธีที่ดีกว่า (กระชับกว่า) ในการลบองค์ประกอบออกจากอาร์เรย์กว่าสิ่งที่ฉันมีที่นี่หรือไม่:

  onRemovePerson: function(index) {
    this.setState(prevState => { // pass callback in setState to avoid race condition
      let newData = prevState.data.slice() //copy array from prevState
      newData.splice(index, 1) // remove element
      return {data: newData} // update state
    })
  },

ขอบคุณ.

อัปเดต

สิ่งนี้ได้รับการปรับปรุงเพื่อใช้โทรกลับใน setState สิ่งนี้ควรทำเมื่ออ้างอิงสถานะปัจจุบันขณะอัพเดต


ลองดู ImmutableJS จาก Facebook ที่ใช้งานได้ดีกับ React ลิงก์
Jonatan Lundqvist Medén

6
ฉันไม่เห็นอะไรผิดปกติกับรหัสของคุณ ในความเป็นจริงมันเป็นวิธีที่งี่เง่ามากที่จะทำ
Dimitar Dimitrov

คำตอบ:


138

วิธีที่สะอาดที่สุดในการทำสิ่งนี้ที่ฉันเคยเห็นคือfilter:

removeItem(index) {
  this.setState({
    data: this.state.data.filter((_, i) => i !== index)
  });
}

18
@HussienK the _บางครั้งใช้เพื่อแสดงอาร์กิวเมนต์ที่ไม่ได้ใช้ นี่คือรายการปัจจุบันในอาร์เรย์
chrisM

1
น่าอัศจรรย์ ฉันใช้. ตัวกรองตลอดเวลา แต่ไม่เคยคิดที่จะใช้มันในสถานการณ์นี้ : D
dakt

4
นี่คือรหัสที่สะอาด แต่สำหรับอาร์เรย์ขนาดใหญ่วิธีนี้จะช้า เหตุผลก็คือมันวนลูปไปทั่วทั้งอาเรย์เพื่อหาดัชนีที่ดูเหมือนจะถูกกำหนดแล้ว
Matt Ellis

1
@MattEllis เนื่องจากการอัพเดทสถานะของ React นั้นไม่เปลี่ยนรูปแบบต่อไปคุณจะต้องเกิด O (n) ในขนาดของรายการเพื่อคัดลอกในกรณีใด ๆ ฉันจะแปลกใจถ้านี่เป็นผลงานที่สำคัญ
ephrion

4
ไม่แนะนำให้ใช้การประเมินthis.stateว่าเป็นอินพุตสำหรับthis.setState()แม้จะไม่สามารถใช้งานได้ โปรดดูคำตอบของฉันข้างต้นสำหรับการอ้างอิงถึงเอกสารตอบโต้อย่างเป็นทางการในหัวข้อนี้
pscl

87

คุณสามารถใช้ตัวupdate()ช่วยที่ไม่เปลี่ยนรูปreact-addons-updateได้ซึ่งทำสิ่งเดียวกันภายใต้ประทุนอย่างมีประสิทธิภาพ แต่สิ่งที่คุณทำอยู่นั้นดี

this.setState(prevState => ({
  data: update(prevState.data, {$splice: [[index, 1]]})
}))

ตัวอย่างง่ายกว่าตัวอย่างที่คุณเชื่อมโยงกับ :)
Koen

7
react-addons-updateเลิกใช้แล้ว (2016) immutability-helperสามารถใช้แทน github.com/kolodny/immutability-helper นอกจากนี้โปรดดูคำตอบของฉันด้านล่างเกี่ยวกับการไม่ทำการเปลี่ยนแปลงนี้ในรัฐโดยตรงใน this.setState ()
pscl

62

ฉันเชื่อว่าการอ้างอิงthis.stateด้านในของsetState()หมดกำลังใจ ( การอัปเดตสถานะอาจไม่ตรงกัน )

เอกสารแนะนำให้ใช้setState()กับฟังก์ชั่นการโทรกลับเพื่อให้ prevState ส่งผ่านเมื่อรันไทม์เมื่อมีการอัพเดท ดังนั้นนี่คือลักษณะ:

ใช้ Array.prototype.filter ที่ไม่มี ES6

removeItem : function(index) {
  this.setState(function(prevState){
    return { data : prevState.data.filter(function(val, i) {
      return i !== index;
    })};
  });
}

ใช้ Array.prototype.filter กับฟังก์ชั่นลูกศร ES6

removeItem(index) {
  this.setState((prevState) => ({
    data: prevState.data.filter((_, i) => i !== index)
  }));
}

การใช้ตัวช่วยไม่เปลี่ยนรูป

import update from 'immutability-helper'
...
removeItem(index) {
  this.setState((prevState) => ({
    data: update(prevState.data, {$splice: [[index, 1]]})
  }))
}

ใช้สเปรด

function removeItem(index) {
  this.setState((prevState) => ({
    data: [...prevState.data.slice(0,index), ...prevState.data.slice(index+1)]
  }))
}

ทราบว่าในแต่ละกรณีโดยไม่คำนึงถึงเทคนิคที่ใช้this.setState()จะถูกส่งโทรกลับ, ไม่อ้างอิงวัตถุเก่าthis.state;


3
นี่คือคำตอบที่ถูกต้องดูพลังของการไม่กลายพันธุ์ข้อมูล
Vinnie James

1
ตัวอย่างการใช้การโทรกลับ prevState ด้วยเทคนิคต่างๆที่เพิ่มเข้ามา
pscl

ถ้าฉันทำแบบนี้ this.setState({ data: [...this.state.data.slice(0, index), ...this.state.data.slice(index + 1)] }); ผิดพลาดหรือไม่ที่จะใช้this.stateแทนprevStateตัวเลือกการโทรกลับที่แสดงโดย @ user1628461
Tiberiu Maxim

นี่เป็นทางออกที่ดีที่สุดแม้ว่าจะไม่ใช่วิธีที่ง่ายที่สุด
Developia

ขอบคุณการใช้ Spread เป็นไปได้ในการอัพเดทองค์ประกอบตรงกลางแทนการลบนั่นคือสิ่งที่ฉันต้องการ
Vaibhav Vishal

24

นี่คือวิธีการลบองค์ประกอบออกจากอาร์เรย์ในสถานะโดยใช้ไวยากรณ์การแพร่กระจาย ES6

onRemovePerson: (index) => {
  const data = this.state.data;
  this.setState({ 
    data: [...data.slice(0,index), ...data.slice(index+1)]
  });
}

1
ขอบคุณ! นี่เป็นวิธีที่เป็นธรรมชาติที่สุดในการทำ
fabiomaia

3

ฉันต้องการที่จะพูดสอดที่นี่แม้ว่าคำถามนี้ได้รับการตอบอย่างถูกต้องแล้วโดย @psclในกรณีที่คนอื่นพบปัญหาเดียวกันกับที่ฉันทำ จากวิธีทั้ง 4 ให้ฉันเลือกที่จะใช้ไวยากรณ์ es6 กับฟังก์ชั่นลูกศรเพราะมันกระชับและขาดการพึ่งพาไลบรารีภายนอก:

ใช้ Array.prototype.filter กับฟังก์ชั่นลูกศร ES6

removeItem(index) {
  this.setState((prevState) => ({
    data: prevState.data.filter((_, i) => i != index)
  }));
}

อย่างที่คุณเห็นฉันทำการปรับเปลี่ยนเล็กน้อยเพื่อละเว้นประเภทของดัชนี ( !==เป็น!=) เพราะในกรณีของฉันฉันได้รับดัชนีจากฟิลด์สตริง

อีกจุดที่เป็นประโยชน์หากคุณเห็นพฤติกรรมแปลก ๆ เมื่อลบองค์ประกอบในฝั่งไคลเอ็นต์คือไม่ต้องใช้ดัชนีของอาร์เรย์เป็นกุญแจสำหรับองค์ประกอบ :

// bad
{content.map((content, index) =>
  <p key={index}>{content.Content}</p>
)}

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

// good
{content.map(content =>
  <p key={content.id}>{content.Content}</p>
)}

ข้างต้นเป็นข้อความที่ตัดตอนมาจากคำตอบนี้จากการโพสต์ที่เกี่ยวข้อง

ทุกคนมีความสุขการเข้ารหัส!


1

คุณสามารถใช้ฟังก์ชั่นนี้หากคุณต้องการลบองค์ประกอบ (ไม่มีดัชนี)

removeItem(item) {
  this.setState(prevState => {
    data: prevState.data.filter(i => i !== item)
  });
}

1

ดังที่ได้กล่าวไว้ในความคิดเห็นต่อคำตอบของ ephrion ข้างต้นตัวกรอง () อาจช้าโดยเฉพาะอย่างยิ่งกับอาร์เรย์ขนาดใหญ่เนื่องจากมีการค้นหาดัชนีที่ดูเหมือนจะถูกกำหนดแล้ว นี่เป็นวิธีการแก้ปัญหาที่สะอาด แต่ไม่มีประสิทธิภาพ

ในฐานะที่เป็นทางเลือกหนึ่งสามารถ 'แยกส่วน' ออกจากองค์ประกอบที่ต้องการและเชื่อมโยงชิ้นส่วน

var dummyArray = [];    
this.setState({data: dummyArray.concat(this.state.data.slice(0, index), this.state.data.slice(index))})

หวังว่านี่จะช่วยได้!


0

คุณสามารถทำให้โค้ดอ่านง่ายขึ้นด้วยฟังก์ชันตัวช่วยหนึ่งบรรทัด:

const removeElement = (arr, i) => [...arr.slice(0, i), ...arr.slice(i+1)];

จากนั้นใช้งานได้เช่น:

this.setState(state => ({ places: removeElement(state.places, index) }));

0

เพียงแค่ข้อเสนอแนะในโค้ดของคุณแทนที่จะใช้let newData = prevState.dataคุณสามารถใช้สเปรดซึ่งเปิดตัวใน ES6 ที่คุณสามารถใช้ได้let newData = ...prevState.data สำหรับการคัดลอกอาเรย์

จุดสามจุด ... หมายถึงผู้ประกอบการแพร่กระจายหรือส่วนที่เหลือพารามิเตอร์ ,

จะช่วยให้การแสดงออกอาร์เรย์หรือสตริงหรืออะไรก็ได้ที่สามารถทำซ้ำที่จะขยายในสถานที่คาดว่าศูนย์หรือมากกว่าอาร์กิวเมนต์สำหรับการเรียกใช้ฟังก์ชั่นหรือองค์ประกอบสำหรับอาร์เรย์ที่คาดว่า

นอกจากนี้คุณสามารถลบรายการจากอาร์เรย์ด้วยการติดตาม

onRemovePerson: function(index) {
  this.setState((prevState) => ({
    data: [...prevState.data.slice(0,index), ...prevState.data.slice(index+1)]
  }))
}

หวังว่านี่จะช่วย !!


-3

นี่เป็นวิธีง่ายๆในการทำ:

removeFunction(key){
  const data = {...this.state.data}; //Duplicate state.
  delete data[key];                  //remove Item form stateCopy.
  this.setState({data});             //Set state as the modify one.
}

หวังว่ามันจะช่วย !!!


สนใจที่จะอธิบาย?
Tiberiu Maxim

5
ฉันคิดว่าเป็นเพราะdeleteลบรายการออก แต่ดัชนีไม่ได้รับการอัปเดตดังนั้นจึงไม่ใช่วิธีที่ดีสำหรับความต้องการทั่วไป
Kunok
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.