แก้ไขการแก้ไขอาร์เรย์ของรัฐใน ReactJS


416

ฉันต้องการเพิ่มองค์ประกอบไปยังจุดสิ้นสุดของstateอาร์เรย์นี่เป็นวิธีที่ถูกต้องหรือไม่

this.state.arrayvar.push(newelement);
this.setState({arrayvar:this.state.arrayvar});

ฉันกังวลว่าการแก้ไขอาร์เรย์ในสถานที่pushอาจทำให้เกิดปัญหา - ปลอดภัยหรือไม่

ทางเลือกในการทำสำเนาของอาร์เรย์และsetStateดูเหมือนว่าสิ้นเปลือง


9
ฉันคิดว่าคุณสามารถใช้ผู้ช่วยเหลือแบบตอบโต้ไม่ได้ ดูสิ่งนี้: facebook.github.io/react/docs/update.html#simple-push
nilgun

setState อยู่ในสถานะตรวจสอบโซลูชันของฉัน <br/> <br> stackoverflow.com/a/59711447/9762736
Rajnikant Lodhi

คำตอบ:


763

React เอกสารพูดว่า:

ปฏิบัติต่อสิ่งนี้รัฐราวกับว่ามันไม่เปลี่ยนรูป

คุณpushจะกลายพันธุ์สถานะโดยตรงและอาจทำให้เกิดข้อผิดพลาดได้ง่ายแม้ว่าคุณจะ "รีเซ็ต" สถานะอีกครั้งในภายหลัง ถ้าเช่นนั้นอาจนำไปสู่วิธีวงจรชีวิตบางอย่างเช่นcomponentDidUpdateจะไม่ทริกเกอร์

วิธีที่แนะนำใน React เวอร์ชั่นต่อมาคือการใช้ฟังก์ชั่นupdaterเมื่อทำการแก้ไขสถานะเพื่อป้องกันสภาวะการแข่งขัน:

this.setState(prevState => ({
  arrayvar: [...prevState.arrayvar, newelement]
}))

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

ไวยากรณ์ทางเลือกสำหรับเวอร์ชัน React รุ่นก่อนหน้า

คุณสามารถใช้concatเพื่อรับไวยากรณ์ใหม่ทั้งหมดเนื่องจากมันจะส่งคืนอาร์เรย์ใหม่:

this.setState({ 
  arrayvar: this.state.arrayvar.concat([newelement])
})

ใน ES6 คุณสามารถใช้ตัวดำเนินการกระจายได้ :

this.setState({
  arrayvar: [...this.state.arrayvar, newelement]
})

8
คุณสามารถให้ตัวอย่างของการแข่งขันที่จะเกิดขึ้นเมื่อใด?
soundly_typed

3
@Qiming pushส่งคืนความยาวของอาร์เรย์ใหม่เพื่อที่จะไม่ทำงาน นอกจากนี้ยังsetStateเป็น async และ React สามารถจัดคิวการเปลี่ยนแปลงหลายสถานะในการแสดงผลครั้งเดียว
David Hellsing

2
@mindeavor บอกว่าคุณมี animationFrame ที่มองหาพารามิเตอร์ใน this.state และวิธีอื่นที่เปลี่ยนพารามิเตอร์อื่น ๆ บางอย่างเกี่ยวกับการเปลี่ยนสถานะ อาจมีบางเฟรมที่สถานะมีการเปลี่ยนแปลง แต่ไม่สะท้อนในวิธีที่รับฟังการเปลี่ยนแปลงเนื่องจาก setState เป็น async
David Hellsing

1
@ChristopherCamps คำตอบนี้ไม่สนับสนุนให้โทรsetStateสองครั้งมันแสดงให้เห็นสองตัวอย่างที่คล้ายกันของการตั้งค่าอาร์เรย์ของรัฐโดยไม่ต้องกลายพันธุ์โดยตรง
David Hellsing

2
วิธีง่ายๆในการจัดการอาร์เรย์ของรัฐในวันนี้ไม่เปลี่ยนรูปคือ: let list = Array.from(this.state.list); list.push('woo'); this.setState({list});ปรับเปลี่ยนสไตล์ที่คุณต้องการแน่นอน
วันธรรมดา

133

ES6ง่ายที่สุดถ้าคุณกำลังใช้

initialArray = [1, 2, 3];

newArray = [ ...initialArray, 4 ]; // --> [1,2,3,4]

อาร์เรย์ใหม่จะเป็น [1,2,3,4]

เพื่ออัปเดตสถานะของคุณในการตอบสนอง

this.setState({
         arrayvar:[...this.state.arrayvar, newelement]
       });

เรียนรู้เพิ่มเติมเกี่ยวกับการทำลายล้างอาร์เรย์


@Muzietto คุณสามารถทำอย่างละเอียด?
StateLess

4
ปมของคำถามนี้มีการเปลี่ยนแปลงสถานะการตอบสนองไม่ได้แก้ไขอาร์เรย์ คุณเห็นจุดของฉันด้วยตัวเองและแก้ไขคำตอบของคุณ นี่ทำให้คำตอบของคุณเกี่ยวข้อง ทำได้ดี.
Marco Faustinelli

2
คำถามของคุณไม่เกี่ยวข้องกับคำถาม OP โดยตรง
StateLess

1
@ChanceSmith: มันเป็นสิ่งจำเป็นในStateLessตอบด้วย อย่าพึ่งพาการอัพเดตสถานะกับสถานะตัวเอง เอกสารอย่างเป็นทางการ: reactjs.org/docs/…
arcol

1
@RayCoder บันทึกและตรวจสอบค่าของ arrayvar ดูเหมือนว่าไม่ใช่อาร์เรย์
StateLess

57

วิธีที่ง่ายที่สุดด้วยES6:

this.setState(prevState => ({
    array: [...prevState.array, newElement]
}))

ขออภัยในกรณีของฉันฉันต้องการที่จะผลักดันอาร์เรย์เป็นอาร์เรย์ หลังจากผลักอาร์เรย์ใหม่ของฉันtableData = [['test','test']] tableData = [['test','test'],['new','new']]วิธีผลัก @David และ @Ridd นี้
Johncy

@Johncy หากคุณต้องการ[['test','test'],['new','new']]ลอง:this.setState({ tableData: [...this.state.tableData, ['new', 'new']]
Ridd

this.setState({ tableData: [...this.state.tableData ,[item.student_name,item.homework_status_name,item.comments===null?'-':item.comments] ] });มันแทรกอาเรย์ใหม่สองครั้ง this.state.tableData.push([item.student_name,item.homework_status_name,item.comments===null?'-':item.comments]);มันบรรลุสิ่งที่ฉันต้องการ แต่มันไม่ใช่วิธีที่ถูกต้องที่ฉันคิด
Johncy

26

ปฏิกิริยาอาจแบตช์อัพเดทและดังนั้นวิธีการที่ถูกต้องคือการให้ setState ด้วยฟังก์ชั่นที่ดำเนินการปรับปรุง

สำหรับ Addact การอัพเดท React ข้อมูลต่อไปนี้จะทำงานได้อย่างน่าเชื่อถือ:

this.setState( state => update(state, {array: {$push: [4]}}) );

หรือสำหรับ concat ():

this.setState( state => ({
    array: state.array.concat([4])
}));

ต่อไปนี้แสดงสิ่งที่https://jsbin.com/mofekakuqi/7/edit?js,outputเป็นตัวอย่างของสิ่งที่เกิดขึ้นหากคุณทำผิด

การเรียกใช้ setTimeout () เพิ่มอย่างถูกต้องสามรายการเนื่องจาก React จะไม่อัปเดตแบตช์ในการโทรกลับ setTimeout (ดูhttps://groups.google.com/d/msg/reactjs/G6pljvpTGX0/0ihYw2zK9dEJ )

buggy onClick จะเพิ่ม "สาม" เท่านั้น แต่อันที่แก้ไขจะเพิ่ม F, S และ T ตามที่คาดไว้

class List extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      array: []
    }

    setTimeout(this.addSome, 500);
  }

  addSome = () => {
      this.setState(
        update(this.state, {array: {$push: ["First"]}}));
      this.setState(
        update(this.state, {array: {$push: ["Second"]}}));
      this.setState(
        update(this.state, {array: {$push: ["Third"]}}));
    };

  addSomeFixed = () => {
      this.setState( state => 
        update(state, {array: {$push: ["F"]}}));
      this.setState( state => 
        update(state, {array: {$push: ["S"]}}));
      this.setState( state => 
        update(state, {array: {$push: ["T"]}}));
    };



  render() {

    const list = this.state.array.map((item, i) => {
      return <li key={i}>{item}</li>
    });
       console.log(this.state);

    return (
      <div className='list'>
        <button onClick={this.addSome}>add three</button>
        <button onClick={this.addSomeFixed}>add three (fixed)</button>
        <ul>
        {list}
        </ul>
      </div>
    );
  }
};


ReactDOM.render(<List />, document.getElementById('app'));

มีกรณีที่เกิดขึ้นจริง ๆ หรือไม่? ถ้าเราทำได้ง่ายๆthis.setState( update(this.state, {array: {$push: ["First", "Second", "Third"]}}) )
Albizia

1
@ Albizia ฉันคิดว่าคุณควรหาเพื่อนร่วมงานและพูดคุยกับพวกเขา ไม่มีปัญหาการแบตช์หากคุณทำการโทร setState เพียงครั้งเดียว จุดคือการแสดงให้เห็นว่าการตอบสนองเป็นชุดการปรับปรุงดังนั้นใช่ ... มีกรณีจริง ๆ ซึ่งเป็นสิ่งที่คุณสามารถหาได้ในรุ่น JSBin ของรหัสข้างต้น คำตอบทั้งหมดในกระทู้นี้ไม่สามารถตอบคำถามนี้ได้ดังนั้นจะมีโค้ดจำนวนมากที่บางครั้งผิดพลาด
NealeU

state.array = state.array.concat([4])สิ่งนี้จะกลายเป็นวัตถุสถานะก่อนหน้า
Emile Bergeron

1
@EmileBergeron ขอบคุณสำหรับความเพียรของคุณ ในที่สุดฉันก็มองกลับไปและเห็นว่าสมองของฉันปฏิเสธที่จะเห็นอะไรและตรวจสอบเอกสารดังนั้นฉันจะแก้ไข
NealeU

1
ดี! เป็นเรื่องง่ายที่จะเข้าใจผิดว่าการไม่เปลี่ยนรูปแบบใน JS นั้นไม่ชัดเจน (ยิ่งเมื่อจัดการกับ API ของไลบรารี)
Emile Bergeron

17

ตามที่ @nilgun กล่าวถึงในความคิดเห็นคุณสามารถใช้ผู้ช่วยเหลือที่ไม่สามารถโต้ตอบได้ ฉันพบว่าสิ่งนี้มีประโยชน์มาก

จากเอกสาร:

ดันง่าย

var initialArray = [1, 2, 3];
var newArray = update(initialArray, {$push: [4]}); // => [1, 2, 3, 4]

initialArray ยังคงเป็น [1, 2, 3]


6
ตัวช่วยในการตอบโต้แบบ React ไม่ได้อธิบายไว้ในเอกสารประกอบ ควรใช้github.com/kolodny/immutability-helperแทน
Periata Breatta

3

หากคุณกำลังใช้งานส่วนประกอบโปรดใช้สิ่งนี้ดังต่อไปนี้

const [chatHistory, setChatHistory] = useState([]); // define the state

const chatHistoryList = [...chatHistory, {'from':'me', 'message':e.target.value}]; // new array need to update
setChatHistory(chatHistoryList); // update the state

1

สำหรับองค์ประกอบใหม่ที่เพิ่มเข้าไปในอาร์เรย์push()ควรเป็นคำตอบ

สำหรับการลบองค์ประกอบและอัพเดทสถานะของอาร์เรย์รหัสด้านล่างใช้งานได้สำหรับฉัน splice(index, 1)ไม่สามารถทำงานได้

const [arrayState, setArrayState] = React.useState<any[]>([]);
...

// index is the index for the element you want to remove
const newArrayState = arrayState.filter((value, theIndex) => {return index !== theIndex});
setArrayState(newArrayState);

0

ฉันพยายามที่จะผลักดันค่าในสถานะอาร์เรย์และตั้งค่าเช่นนี้และกำหนดอาร์เรย์ของรัฐและมูลค่าการผลักดันโดยฟังก์ชั่นแผนที่

 this.state = {
        createJob: [],
        totalAmount:Number=0
    }


 your_API_JSON_Array.map((_) => {
                this.setState({totalAmount:this.state.totalAmount += _.your_API_JSON.price})
                this.state.createJob.push({ id: _._id, price: _.your_API_JSON.price })
                return this.setState({createJob: this.state.createJob})
            })

0

สิ่งนี้ทำงานสำหรับฉันที่จะเพิ่มอาร์เรย์ภายในอาร์เรย์

this.setState(prevState => ({
    component: prevState.component.concat(new Array(['new', 'new']))
}));

0

นี่คือตัวอย่างของ Reactjs Hook ปี 2020 ที่ฉันคิดว่าสามารถช่วยเหลือผู้อื่นได้ ฉันใช้มันเพื่อเพิ่มแถวใหม่ลงในตาราง Reactjs แจ้งให้เราทราบหากฉันสามารถปรับปรุงบางสิ่งได้

การเพิ่มองค์ประกอบใหม่ให้กับองค์ประกอบสถานะการทำงาน:

กำหนดข้อมูลสถานะ:

    const [data, setData] = useState([
        { id: 1, name: 'John', age: 16 },
        { id: 2, name: 'Jane', age: 22 },
        { id: 3, name: 'Josh', age: 21 }
    ]);

มีปุ่มเปิดใช้งานฟังก์ชั่นเพื่อเพิ่มองค์ประกอบใหม่

<Button
    // pass the current state data to the handleAdd function so we can append to it.
    onClick={() => handleAdd(data)}>
    Add a row
</Button>
function handleAdd(currentData) {

        // return last data array element
        let lastDataObject = currentTableData[currentTableData.length - 1]

        // assign last elements ID to a variable.
        let lastID = Object.values(lastDataObject)[0] 

        // build a new element with a new ID based off the last element in the array
        let newDataElement = {
            id: lastID + 1,
            name: 'Jill',
            age: 55,
        }

        // build a new state object 
        const newStateData = [...currentData, newDataElement ]

        // update the state
        setData(newStateData);

        // print newly updated state
        for (const element of newStateData) {
            console.log('New Data: ' + Object.values(element).join(', '))
        }

}

-1
this.setState({
  arrayvar: [...this.state.arrayvar, ...newelement]
})

2
stackoverflowเพิ่มคำอธิบายบางอย่างเพื่อหลีกเลี่ยงการโพสต์นี้จะต้องพิจารณาเป็นโพสต์ที่มีคุณภาพต่ำโดย
Harsha Biyani

2
@Kobe การโทรนี้ "ผู้ดำเนินการแพร่กระจาย" ใน ES6 และผนวกรายการ array2 ไปยัง array1 ขอบคุณสำหรับ downvote (:
M.Samiei

@ M.Samiei คุณต้องแก้ไขโพสต์ของคุณเพื่อหลีกเลี่ยงการลงคะแนน มันมีคุณภาพต่ำเพียงแค่เป็นข้อมูลโค้ด นอกจากนี้ยังเป็นอันตรายที่จะแก้ไขด้วยวิธีนี้เนื่องจากอาจมีการอัพเดตเป็นชุดดังนั้นวิธีที่ต้องการคือsetState(state => ({arrayvar: [...state.arrayvar, ...newelement]}) );
NealeU

และกระจายnewelementวัตถุภายในอาเรย์จะส่งTypeErrorต่อไป
Emile Bergeron

-1
//------------------code is return in typescript 

const updateMyData1 = (rowIndex:any, columnId:any, value:any) => {

    setItems(old => old.map((row, index) => {
        if (index === rowIndex) {
        return Object.assign(Object.assign({}, old[rowIndex]), { [columnId]: value });
    }
    return row;
}));

-6

รหัสนี้ใช้ได้กับฉัน:

fetch('http://localhost:8080')
  .then(response => response.json())
  .then(json => {
    this.setState({mystate: this.state.mystate.push.apply(this.state.mystate, json)})
  })

2
ยังคุณเปลี่ยนสถานะโดยตรง
Asbar Ali

2
และไม่สามารถอัพเดทสถานะขององค์ประกอบได้อย่างถูกต้องตั้งแต่.pushส่งกลับตัวเลขไม่ใช่อาร์เรย์
Emile Bergeron
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.