Cách Xử lý State bất biến(Immutable) cần lưu ý.

3 min read

Có lẽ có sự nhầm lẫn phổ biến nhất trong React hiện nay là khi xử lý State.

Hãy tưởng tượng bạn có một Form để chỉnh sửa người dùng. Thông thường, bạn sẽ tạo một trình xử lý thay đổi duy nhất để xử lý các thay đổi trên tất cả các trường của Form. Đoạn mã có thể trông như sau:

updateState(event) {
        const { name, value } = event.target;
        let user = this.state.user; // this is a reference, not a copy...
        user[name] = value; // so this mutates state ?
        return this.setState({ user });
    }

Vấn đề ở dòng số 4. Thực tế, dòng số 4 làm biến đổi state vì biến user là một tham chiếu đến state (reference to state). State trong React nên được xử lý như là bất biến.

Theo tài liệu từ React docs

Không bao giờ thay đổi this.state trực tiếp, vì sau đó gọi setState() có thể làm thay thế sự thay đổi bạn vừa thực hiện. Hãy xử lý this.state như là bất biến(immutable).

Vì sao?

Hàm setState tự động nhóm các công việc lại phía sau bí mật. Điều này có nghĩa là một sự thay đổi trạng thái thủ công có thể bị ghi đè khi setState được xử lý.

Nếu bạn khai báo một phương thức shouldComponentUpdate, bạn không thể sử dụng condition “===” bên trong vì tham chiếu đối tượng(object reference) sẽ không thay đổi. Vì vậy, cách tiếp cận như trên có thể ảnh hưởng đến hiệu suất.

Tóm lại: Ví dụ trên thường hoạt động tốt, nhưng để tránh các trường hợp phức tạp , hãy xử lý trạng thái như là bất biến.

Dưới đây là bốn cách để xử lý State như là bất biến:

1. Object.assign

Object.assign tạo ra một bản sao của một đối tượng. Tham số đầu tiên là đối tượng đích, sau đó bạn chỉ định một hoặc nhiều tham số khác cho các thuộc tính mà bạn muốn thêm vào. Vì vậy, để sửa đoạn mã ví dụ ở trên, bạn chỉ cần thay đổi đơn giản dòng số 3:

updateState(event) {
        const { name, value } = event.target;
        let user = Object.assign({}, this.state.user);
        user[name] = value;
        return this.setState({ user });
 }

Ở dòng số 3, chúng ta “Tạo một đối tượng user rỗng mới và đã bao gồm tất cả các thuộc tính từ this.state.user vào đối tượng mới này.” Điều này tạo ra một bản sao riêng biệt của đối tượng user được lưu trữ trong state. Bây giờ tôi có thể yên tâm thay đổi đối tượng user ở dòng số 4 — nó là một đối tượng hoàn toàn riêng biệt so với đối tượng trong trạng thái.

Hãy chắc chắn làm polyfill cho Object.assign vì nó không được hỗ trợ trong IE và không được transpile bởi Babel.

2. Object Spread

Object Spread của đối tượng hiện đang ở giai đoạn 3 và có thể được transpile bởi Babel. Cách tiếp cận này ngắn gọn hơn

updateState(event) {
        const { name, value } = event.target;
        let user = { ...this.state.user, [name]: value };
        this.setState({ user });
}

Ở dòng số 3, chúng ta “Sử dụng tất cả các thuộc tính từ this.state.user để tạo một đối tượng mới, sau đó thiết lập thuộc tính được biểu diễn bởi [name] thành giá trị mới được truyền từ event.target.value”. Vì vậy, cách tiếp cận này hoạt động tương tự như cách sử dụng Object.assign, nhưng có hai lợi ích:

+ Không cần polyfill, vì Babel có thể transpile
+ Ngắn gọn hơn

Bạn có thể thậm chí sử dụng destructuring và inline để làm cho đoạn mã này chỉ một dòng:

Tôi sử dụng destructuring trong method signature để lấy tham chiếu đến event.target. Sau đó, tôi khai báo rằng state user nên được thiết lập thành một bản sao của this.state.user với thuộc tính liên quan được thiết lập thành giá trị mới. Tôi thích cách viết ngắn gọn này. Hiện tại, đây là cách tiếp cận yêu thích nhất của tôi để xử lý immutable state.

Avatar photo

Leave a Reply

Your email address will not be published. Required fields are marked *