Sửa đổi chính xác các mảng trạng thái trong ReactJS


416

Tôi muốn thêm một phần tử vào cuối một statemảng, đây có phải là cách chính xác để làm điều đó?

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

Tôi lo ngại rằng việc sửa đổi mảng tại chỗ có pushthể gây rắc rối - có an toàn không?

Việc thay thế tạo một bản sao của mảng và setStateing có vẻ lãng phí.


9
Tôi nghĩ rằng bạn có thể sử dụng phản ứng giúp đỡ bất biến. xem cái này: facebook.github.io/react/docs/update.html#simple-push
nilgun

setState trong mảng trạng thái kiểm tra giải pháp của tôi. <br/> <br> stackoverflow.com/a/59711447/9762736
Rajnikant Lodhi

Câu trả lời:


763

Các tài liệu React nói:

Hãy đối xử với điều này.state như thể nó là bất biến.

Ý chí của bạn pushsẽ thay đổi trạng thái trực tiếp và điều đó có khả năng dẫn đến mã dễ bị lỗi, ngay cả khi bạn "đặt lại" trạng thái đó sau đó. F.ex, nó có thể dẫn đến một số phương pháp vòng đời như componentDidUpdatesẽ không kích hoạt.

Cách tiếp cận được đề xuất trong các phiên bản React sau này là sử dụng chức năng cập nhật khi sửa đổi trạng thái để ngăn chặn điều kiện cuộc đua:

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

Bộ nhớ "lãng phí" không phải là vấn đề so với các lỗi bạn có thể gặp phải khi sử dụng các sửa đổi trạng thái không chuẩn.

Cú pháp thay thế cho các phiên bản React trước đó

Bạn có thể sử dụng concatđể có được một cú pháp rõ ràng vì nó trả về một mảng mới:

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

Trong ES6, bạn có thể sử dụng Toán tử trải rộng :

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

8
Bạn có thể cung cấp một ví dụ về khi một điều kiện cuộc đua sẽ xảy ra?
soundly_typed

3
@Qiming pushtrả về độ dài mảng mới để không hoạt động. Ngoài ra, setStatelà async và React có thể xếp hàng một số thay đổi trạng thái vào một lượt kết xuất duy nhất.
David Hellsing

2
@mindeavor nói rằng bạn có một AnimationFrame tìm kiếm các tham số trong this.state và một phương thức khác thay đổi một số tham số khác khi thay đổi trạng thái. Có thể có một số khung trong đó trạng thái đã thay đổi nhưng không được phản ánh trong phương thức lắng nghe thay đổi, vì setState không đồng bộ.
David Hellsing

1
@ChristopherCamp Câu trả lời này không khuyến khích gọi setStatehai lần, nó cho thấy hai ví dụ tương tự về thiết lập mảng trạng thái mà không làm thay đổi trực tiếp.
David Hellsing

2
Một cách dễ dàng để coi một mảng trạng thái là bất biến ngày nay là: let list = Array.from(this.state.list); list.push('woo'); this.setState({list});Sửa đổi theo sở thích phong cách của bạn tất nhiên.
thứ

133

Dễ nhất, nếu bạn đang sử dụng ES6.

initialArray = [1, 2, 3];

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

Mảng mới sẽ là [1,2,3,4]

để cập nhật trạng thái của bạn trong React

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

Tìm hiểu thêm về phá hủy mảng


@Muzietto bạn có thể giải thích?
StateLess

4
Mấu chốt của câu hỏi này là thay đổi trạng thái React, không sửa đổi mảng. Bạn đã nhìn thấy quan điểm của tôi một mình và chỉnh sửa câu trả lời của bạn. Điều này làm cho câu trả lời của bạn có liên quan. Làm tốt.
Marco Faustinelli

2
Câu hỏi của bạn không liên quan trực tiếp
StateLess

1
@ChanceSmith: cũng cần trong câu trả lời của StateLess . Không phụ thuộc vào cập nhật trạng thái trên chính nhà nước. Tài liệu chính thức: Reacjs.org/docs/ trộm
arcol 26/12/18

1
@RayCoder đăng nhập và kiểm tra giá trị của mảng, có vẻ như nó không phải là mảng.
StateLess

57

Cách đơn giản nhất với ES6:

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

Xin lỗi, trong trường hợp của tôi, tôi muốn đẩy một mảng thành mảng. tableData = [['test','test']]Sau khi đẩy mảng mới của tôi tableData = [['test','test'],['new','new']]. làm thế nào để đẩy cái này @David và @Ridd
Johncy

@Johncy Nếu bạn muốn [['test','test'],['new','new']]dùng thử: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] ] });Nó chèn mảng mới hai lần this.state.tableData.push([item.student_name,item.homework_status_name,item.comments===null?'-':item.comments]);Nó đạt được điều tôi muốn. Nhưng nó không phải là cách tôi nghĩ đúng.
Johncy

26

React có thể cập nhật hàng loạt và do đó, cách tiếp cận đúng là cung cấp cho setState một chức năng thực hiện cập nhật.

Đối với addon cập nhật React, phần sau sẽ hoạt động đáng tin cậy:

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

hoặc cho concat ():

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

Sau đây cho thấy https://jsbin.com/mofekakuqi/7/edit?js,output là một ví dụ về những gì xảy ra nếu bạn hiểu sai.

Lệnh gọi setTimeout () thêm chính xác ba mục vì React sẽ không cập nhật hàng loạt trong một cuộc gọi lại setTimeout (xem https://groups.google.com/d/msg/reactjs/G6pljvpTGX0/0ihYw2zK9dEJ ).

Lỗi onClick sẽ chỉ thêm "Thứ ba", nhưng lỗi cố định, sẽ thêm F, S và T như mong đợi.

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

Có thực sự có một trường hợp nó xảy ra? Nếu chúng ta chỉ đơn giản làmthis.setState( update(this.state, {array: {$push: ["First", "Second", "Third"]}}) )
Albizia

1
@Albizia Tôi nghĩ bạn nên tìm một đồng nghiệp và thảo luận với họ. Không có vấn đề theo đợt nếu bạn chỉ thực hiện một cuộc gọi setState. Vấn đề là chỉ ra rằng các bản cập nhật các đợt React, vì vậy vâng ... thực sự có một trường hợp, đó là những gì bạn có thể tìm thấy trong phiên bản JSBin của đoạn mã trên. Hầu như tất cả các câu trả lời trong chủ đề này đều không giải quyết được vấn đề này, vì vậy sẽ có rất nhiều mã ngoài kia đôi khi bị sai
NealeU

state.array = state.array.concat([4])điều này làm thay đổi đối tượng trạng thái trước đó.
Emile Bergeron

1
@EmileBergeron Cảm ơn sự kiên trì của bạn. Cuối cùng tôi đã nhìn lại và thấy những gì bộ não của tôi đã từ chối nhìn thấy, và kiểm tra các tài liệu, vì vậy tôi sẽ chỉnh sửa.
NealeU

1
Tốt Thật sự rất dễ hiểu sai vì tính bất biến trong JS là không rõ ràng (thậm chí còn hơn thế khi xử lý API của thư viện).
Emile Bergeron

17

Như @nilgun đã đề cập trong bình luận, bạn có thể sử dụng các trợ giúp bất biến phản ứng . Tôi thấy điều này là siêu hữu ích.

Từ các tài liệu:

Đẩy đơn giản

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

initArray vẫn là [1, 2, 3].


6
Các trợ giúp bất biến React được mô tả là không dùng nữa trong tài liệu. github.com/kolodny/immutability-helper nên được sử dụng thay thế.
Periata Breatta

3

Nếu bạn đang sử dụng thành phần chức năng xin vui lòng sử dụng này như dưới đây.

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

Để thêm phần tử mới vào mảng, push()nên là câu trả lời.

Để loại bỏ phần tử và trạng thái cập nhật của mảng, mã bên dưới hoạt động với tôi. splice(index, 1)không thể làm việc.

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

Tôi đang cố gắng đẩy giá trị trong trạng thái mảng và đặt giá trị như thế này và xác định mảng trạng thái và giá trị đẩy theo chức năng bản đồ.

 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

Điều này làm việc cho tôi để thêm một mảng trong một mảng

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

0

Đây là một ví dụ năm 2020, Reactjs Hook mà tôi nghĩ có thể giúp đỡ người khác. Tôi đang sử dụng nó để thêm các hàng mới vào bảng Reactjs. Hãy cho tôi biết nếu tôi có thể cải thiện một cái gì đó.

Thêm một phần tử mới vào một thành phần trạng thái chức năng:

Xác định dữ liệu trạng thái:

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

Có một nút kích hoạt một chức năng để thêm một yếu tố mới

<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
Thêm một số giải thích để tránh bài đăng này để coi là bài chất lượng thấp bởi stackoverflow.
Harsha Biyani

2
@Kobe cuộc gọi này "toán tử lây lan" trong ES6 và nối các mục mảng2 vào mảng1. cảm ơn vì downvote (:
M.Samiei

@ M.Samiei Bạn cần chỉnh sửa bài đăng của mình để tránh bị downvote. Đó là chất lượng thấp như một đoạn mã. Ngoài ra, nó là nguy hiểm để thay đổi trạng thái theo cách này như cập nhật có thể được trộn, vì vậy cách ưa thích là ví dụsetState(state => ({arrayvar: [...state.arrayvar, ...newelement]}) );
NealeU

và lan truyền newelementđối tượng trong một mảng sẽ ném ra 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

Mã này hoạt động với tôi:

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

2
Tuy nhiên, bạn sẽ thay đổi trạng thái trực tiếp
Asbar Ali

2
Và không cập nhật chính xác trạng thái của thành phần vì .pushtrả về một số , không phải là một mảng.
Emile Bergeron
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.