Lưu trữ một đối tượng trong trạng thái của một thành phần React?


76

Có thể lưu trữ một đối tượng ở trạng thái của một thành phần React không? Nếu có, thì chúng ta có thể thay đổi giá trị của một khóa trong đối tượng đó bằng cách setStatenào? Tôi nghĩ về mặt cú pháp không được phép viết những thứ như:

this.setState({ abc.xyz: 'new value' });

Trên các dòng tương tự, tôi có một câu hỏi khác: Có ổn không khi có một tập hợp các biến trong một thành phần React để chúng có thể được sử dụng trong bất kỳ phương thức nào của thành phần đó, thay vì lưu trữ chúng trong một trạng thái?

Bạn có thể tạo một đối tượng đơn giản chứa tất cả các biến này và đặt nó ở cấp thành phần, giống như cách bạn khai báo bất kỳ phương thức nào trên thành phần.

Nó rất có thể gặp phải các tình huống trong đó bạn đưa nhiều logic nghiệp vụ vào mã của mình và yêu cầu sử dụng nhiều biến có giá trị được thay đổi bằng một số phương pháp và sau đó bạn thay đổi trạng thái của thành phần dựa trên các giá trị này.

Vì vậy, thay vì giữ tất cả các biến đó ở trạng thái, bạn chỉ giữ những biến có giá trị phải được phản ánh trực tiếp trong giao diện người dùng.

Nếu cách tiếp cận này tốt hơn câu hỏi đầu tiên tôi đã viết ở đây, thì tôi không cần phải lưu trữ một đối tượng trong trạng thái.

Câu trả lời:


38

Ngoài bài đăng của kiran, còn có trình trợ giúp cập nhật (trước đây là một addon phản ứng ). Điều này có thể được cài đặt với npm bằng cách sử dụngnpm install immutability-helper

import update from 'immutability-helper';

var abc = update(this.state.abc, {
   xyz: {$set: 'foo'}
});

this.setState({abc: abc});

Điều này tạo ra một đối tượng mới với giá trị được cập nhật và các thuộc tính khác được giữ nguyên. Điều này hữu ích hơn khi bạn cần thực hiện những việc như đẩy vào một mảng và đặt một số giá trị khác cùng một lúc. Một số người sử dụng nó ở mọi nơi vì nó cung cấp tính bất biến.

Nếu bạn làm điều này, bạn có thể có những thứ sau để bù đắp cho hiệu suất của

shouldComponentUpdate: function(nextProps, nextState){
   return this.state.abc !== nextState.abc; 
   // and compare any props that might cause an update
}

Vì vậy, tôi đang tưởng tượng sẽ dễ dàng hơn nhiều nếu chỉ lưu trữ trạng thái trong một thứ gì đó bên cạnh this.state, và gọi render()bất cứ lúc nào bạn thay đổi thứ gì đó bên trong nó; điều đó có được coi là hoàn toàn không có trong số các nhà phát triển React có kinh nghiệm không?
Andy

render là một chức năng thuần túy, nó không thực sự làm bất cứ điều gì. Nó được gọi bằng phản ứng khi có bản cập nhật và các bản cập nhật được thực hiện bởi lệnh gọi setState. Các phương pháp hay nhất cho rằng kết xuất của bạn phải tạo ra cùng một đầu ra cho cùng một đạo cụ và trạng thái. Điều này cũng cho phép các tối ưu hóa như shouldComponentUpdate trở nên đơn giản.
Brigand

Chà, tôi xa SO lâu chưa? Tôi thực sự đã sử dụng một số công cụ phản ứng cơ bản để gọi forceUpdate()bất cứ khi nào các trường nhất định của mô hình Backbone thay đổi. Do đó, tôi có thể ghi đè đơn giản shouldComponentUpdateđể trả về false. rendervẫn tạo ra cùng một đầu ra cho cùng một props"trạng thái" (tức là các mô hình Backbone, tôi không lưu trữ bất cứ thứ gì trong đó this.state).
Andy

1
@Sebastialonso require('react-addons-update')và cài đặt nó tất nhiên.
Brigand

1
Có vẻ như helper này bây giờ là di sản, và thư viện này được khuyến khích thay vì: github.com/kolodny/immutability-helper
asiop

88
  1. this.setState({ abc.xyz: 'new value' });cú pháp không được phép. Bạn phải vượt qua toàn bộ đối tượng.

    this.setState({abc: {xyz: 'new value'}});
    

    Nếu bạn có các biến khác trong abc

    var abc = this.state.abc;
    abc.xyz = 'new value';
    this.setState({abc: abc});
    
  2. Bạn có thể có các biến thông thường, nếu chúng không dựa vào this.props và this.state.


13
Nếu bạn muốn giữ lại các tài sản khác trong đối tượng, một phương pháp như của gạch mở rộng rất hữu ích:this.setState({abc: _.extend(this.state.abc, {xyz: 'new value'})});
smhg

1
@smhg không hoàn toàn, updatenó mạnh hơn ở chỗ nó hỗ trợ các lệnh để sửa đổi các đối tượng phức tạp bên trong trạng thái. Khi bạn không cần những tính năng đặc biệt này, tôi nghĩ _.extendsẽ cung cấp một cú pháp gọn gàng hơn nhiều, đặc biệt là đối với những người đọc mã muộn hơn và không quen với mục đích của nó update.
Zoltán

2
@smhg, để thêm vào như của bạn, bạn có thể sử dụng Object.assignnếu trình duyệt của bạn hỗ trợ ES6 (hoặc chỉ cần sử dụng một polyfill)
Jay

1
@kiran thiết lập một đối tượng phức tạp cho một biến KHÔNG sao chép nó, nó chỉ đơn giản là tạo một tham chiếu khác đến cùng một đối tượng. Vì vậy, var abc = this.state.abc; abc.xyz = 'new value'; cũng giống như this.state.abc.xyz='new value'
jasonseminara

1
Câu trả lời này trông có vẻ đáng ngờ như OP đang sửa đổi trạng thái trực tiếp, đây là một điều không nên. Giả sử rằng abcchỉ có lĩnh vực nguyên thủy, dòng đầu tiên thích hợp nênvar abc = { ...this.state.abc };
Chris G

47

Bạn có thể sử dụng ES6 lây lan trên các giá trị trước đó trong đối tượng để tránh ghi đè

this.setState({
     abc: {
            ...this.state.abc,
            xyz: 'new value'
           }
});

15

this.setState({abc: {xyz: 'new value'}});sẽ KHÔNG hoạt động, vì state.abcsẽ được ghi đè hoàn toàn, không được hợp nhất.

Điều này phù hợp với tôi:

this.setState((previousState) => {
  previousState.abc.xyz = 'blurg';
  return previousState;
});

Trừ khi tôi đọc nhầm tài liệu, Facebook đề xuất định dạng trên. https://facebook.github.io/react/docs/component-api.html

Ngoài ra, tôi đoán cách trực tiếp nhất mà không gây đột biến trạng thái là sao chép trực tiếp bằng cách sử dụng toán tử spread / rest của ES6:

const newState = { ...this.state.abc }; // deconstruct state.abc into a new object-- effectively making a copy
newState.xyz = 'blurg';
this.setState(newState);

Tôi đã thử nghiệm cách đầu tiên của bạn bằng cách sử dụng componentDidUpdate()và nó đã giữ đúng trạng thái trước đó trong khi thực hiện this.setState({[abc.xyz]: 'blurg'})đưa ra trạng thái trước đó không chính xác nên tôi cho rằng nó tốt.
Martin Dawson

Trạng thái trước không thực sự là Trạng thái trước đó. Đó là Trạng thái hiện tại, không chính xác.
Martin Dawson


previousState.abc.xyz = 'blurg';thay đổi trạng thái hiện tại vốn là một phản mẫu trong React.
Emile Bergeron

8

Mặc dù nó có thể được thực hiện thông qua trình trợ giúp không thay đổi hoặc tương tự, tôi không thể thêm các phụ thuộc bên ngoài vào mã của mình trừ khi tôi thực sự phải làm. Khi tôi cần làm điều đó, tôi sử dụng Object.assign. Mã:

this.setState({ abc : Object.assign({}, this.state.abc , {xyz: 'new value'})})

Cũng có thể được sử dụng trên Thuộc tính sự kiện HTML, ví dụ:

onChange={e => this.setState({ abc : Object.assign({}, this.state.abc, {xyz : 'new value'})})}

8

Cách dễ dàng hơn để làm điều đó trong một dòng mã

this.setState({ object: { ...this.state.object, objectVarToChange: newData } })
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.