Các bài viết được liệt kê trong đưa ra một số ví dụ hay về cách thực hiện các thao tác cập nhật cơ bản không thay đổi, chẳng hạn như cập nhật một trường trong một đối tượng hoặc thêm một mục vào cuối một mảng. Tuy nhiên, bộ giảm tốc thường sẽ cần sử dụng kết hợp các thao tác cơ bản đó để thực hiện các tác vụ phức tạp hơn. Dưới đây là một số ví dụ cho một số nhiệm vụ phổ biến hơn mà bạn có thể phải triển khai
Cập nhật các đối tượng lồng nhau
Chìa khóa để cập nhật dữ liệu lồng nhau là mọi cấp độ lồng nhau phải được sao chép và cập nhật một cách thích hợp. Đây thường là một khái niệm khó đối với những người học Redux và có một số vấn đề cụ thể thường xảy ra khi cố gắng cập nhật các đối tượng lồng nhau. Những điều này dẫn đến đột biến trực tiếp ngẫu nhiên và nên tránh
Cách tiếp cận đúng. Sao chép tất cả các cấp dữ liệu lồng nhauThật không may, quá trình áp dụng chính xác các bản cập nhật bất biến cho trạng thái được lồng sâu có thể dễ dàng trở nên dài dòng và khó đọc. Đây là ví dụ về việc cập nhật
function updateNestedState[state, action] {
let nestedState = state.nestedState
// ERROR: this directly modifies the existing object reference - don't do this!
nestedState.nestedField = action.data
return {
...state,
nestedState
}
}
1 có thể trông như thế nàofunction updateVeryNestedField[state, action] {
return {
...state,
first: {
...state.first,
second: {
...state.first.second,
[action.someId]: {
...state.first.second[action.someId],
fourth: action.someValue
}
}
}
}
}
Rõ ràng, mỗi lớp lồng làm cho điều này khó đọc hơn và có nhiều cơ hội mắc lỗi hơn. Đây là một trong nhiều lý do tại sao bạn được khuyến khích giữ cho trạng thái của mình phẳng và soạn các bộ giảm tốc càng nhiều càng tốt
Sai lầm phổ biến #1. Các biến mới trỏ đến cùng một đối tượngXác định một biến mới không tạo ra một đối tượng thực tế mới - nó chỉ tạo ra một tham chiếu khác cho cùng một đối tượng. Một ví dụ về lỗi này sẽ là
function updateNestedState[state, action] {
let nestedState = state.nestedState
// ERROR: this directly modifies the existing object reference - don't do this!
nestedState.nestedField = action.data
return {
...state,
nestedState
}
}
Hàm này trả về chính xác một bản sao nông của đối tượng trạng thái cấp cao nhất, nhưng vì biến
function updateNestedState[state, action] {
let nestedState = state.nestedState
// ERROR: this directly modifies the existing object reference - don't do this!
nestedState.nestedField = action.data
return {
...state,
nestedState
}
}
2 vẫn trỏ vào đối tượng hiện có nên trạng thái đã bị thay đổi trực tiếpSai lầm thường gặp #2. Chỉ tạo một bản sao nông của một cấpMột phiên bản phổ biến khác của lỗi này trông như thế này
function updateNestedState[state, action] {
// Problem: this only does a shallow copy!
let newState = { ...state }
// ERROR: nestedState is still the same object!
newState.nestedState.nestedField = action.data
return newState
}
Sao chép nông ở cấp cao nhất là không đủ - đối tượng
function updateNestedState[state, action] {
let nestedState = state.nestedState
// ERROR: this directly modifies the existing object reference - don't do this!
nestedState.nestedField = action.data
return {
...state,
nestedState
}
}
2 cũng nên được sao chépChèn và xóa các mục trong mảng
Thông thường, nội dung của mảng Javascript được sửa đổi bằng cách sử dụng các hàm thay đổi như
function updateNestedState[state, action] {
let nestedState = state.nestedState
// ERROR: this directly modifies the existing object reference - don't do this!
nestedState.nestedField = action.data
return {
...state,
nestedState
}
}
4, function updateNestedState[state, action] {
let nestedState = state.nestedState
// ERROR: this directly modifies the existing object reference - don't do this!
nestedState.nestedField = action.data
return {
...state,
nestedState
}
}
5 và function updateNestedState[state, action] {
let nestedState = state.nestedState
// ERROR: this directly modifies the existing object reference - don't do this!
nestedState.nestedField = action.data
return {
...state,
nestedState
}
}
6. Vì chúng tôi không muốn thay đổi trạng thái trực tiếp trong bộ giảm tốc, nên thường nên tránh những trạng thái đó. Do đó, bạn có thể thấy hành vi "chèn" hoặc "xóa" được viết như thế nàyfunction insertItem[array, action] {
return [
...array.slice[0, action.index],
action.item,
...array.slice[action.index]
]
}
function removeItem[array, action] {
return [...array.slice[0, action.index], ...array.slice[action.index + 1]]
}
Tuy nhiên, hãy nhớ rằng điều quan trọng là tham chiếu trong bộ nhớ ban đầu không bị sửa đổi. Miễn là chúng tôi tạo một bản sao trước, chúng tôi có thể thay đổi bản sao một cách an toàn. Lưu ý rằng điều này đúng với cả mảng và đối tượng, nhưng các giá trị lồng nhau vẫn phải được cập nhật bằng cùng một quy tắc
Điều này có nghĩa là chúng ta cũng có thể viết các hàm chèn và xóa như thế này
function updateVeryNestedField[state, action] {
return {
...state,
first: {
...state.first,
second: {
...state.first.second,
[action.someId]: {
...state.first.second[action.someId],
fourth: action.someValue
}
}
}
}
}
0Chức năng loại bỏ cũng có thể được thực hiện như
function updateVeryNestedField[state, action] {
return {
...state,
first: {
...state.first,
second: {
...state.first.second,
[action.someId]: {
...state.first.second[action.someId],
fourth: action.someValue
}
}
}
}
}
1Cập nhật một mục trong một mảng
Việc cập nhật một mục trong một mảng có thể được thực hiện bằng cách sử dụng
function updateNestedState[state, action] {
let nestedState = state.nestedState
// ERROR: this directly modifies the existing object reference - don't do this!
nestedState.nestedField = action.data
return {
...state,
nestedState
}
}
7, trả về một giá trị mới cho mục chúng tôi muốn cập nhật và trả về các giá trị hiện có cho tất cả các mục khácfunction updateVeryNestedField[state, action] {
return {
...state,
first: {
...state.first,
second: {
...state.first.second,
[action.someId]: {
...state.first.second[action.someId],
fourth: action.someValue
}
}
}
}
}
3Thư viện tiện ích cập nhật bất biến
Bởi vì việc viết mã cập nhật bất biến có thể trở nên tẻ nhạt, nên có một số thư viện tiện ích cố gắng trừu tượng hóa quy trình. Các thư viện này khác nhau về API và cách sử dụng, nhưng tất cả đều cố gắng cung cấp cách viết những cập nhật này ngắn gọn và súc tích hơn. Ví dụ: Immer làm cho các bản cập nhật bất biến trở thành một hàm đơn giản và các đối tượng JavaScript đơn giản
function updateVeryNestedField[state, action] {
return {
...state,
first: {
...state.first,
second: {
...state.first.second,
[action.someId]: {
...state.first.second[action.someId],
fourth: action.someValue
}
}
}
}
}
4Một số, như dot-prop-immutable, lấy đường dẫn chuỗi cho các lệnh
function updateVeryNestedField[state, action] {
return {
...state,
first: {
...state.first,
second: {
...state.first.second,
[action.someId]: {
...state.first.second[action.someId],
fourth: action.someValue
}
}
}
}
}
5Những thứ khác, như immutability-helper [một nhánh của addon React Immutability Helpers hiện không dùng nữa], sử dụng các giá trị lồng nhau và các hàm trợ giúp
function updateVeryNestedField[state, action] {
return {
...state,
first: {
...state.first,
second: {
...state.first.second,
[action.someId]: {
...state.first.second[action.someId],
fourth: action.someValue
}
}
}
}
}
6Họ có thể cung cấp một giải pháp thay thế hữu ích để viết logic cập nhật bất biến thủ công
Có thể tìm thấy danh sách nhiều tiện ích cập nhật bất biến trong phần Danh mục Redux Addons
Đơn giản hóa các bản cập nhật không thay đổi với Bộ công cụ Redux
Gói Bộ công cụ Redux của chúng tôi bao gồm tiện ích
function updateNestedState[state, action] {
let nestedState = state.nestedState
// ERROR: this directly modifies the existing object reference - don't do this!
nestedState.nestedField = action.data
return {
...state,
nestedState
}
}
8 sử dụng Immer trong nội bộ. Do đó, bạn có thể viết các bộ giảm tốc có vẻ như ở trạng thái "đột biến", nhưng các bản cập nhật thực sự được áp dụng một cách bất biếnĐiều này cho phép logic cập nhật bất biến được viết theo cách đơn giản hơn nhiều. Đây là những gì có thể trông giống như sử dụng
function updateNestedState[state, action] {
let nestedState = state.nestedState
// ERROR: this directly modifies the existing object reference - don't do this!
nestedState.nestedField = action.data
return {
...state,
nestedState
}
}
8function updateNestedState[state, action] {
let nestedState = state.nestedState
// ERROR: this directly modifies the existing object reference - don't do this!
nestedState.nestedField = action.data
return {
...state,
nestedState
}
}
0Điều này rõ ràng ngắn hơn nhiều và dễ đọc hơn. Tuy nhiên, điều này chỉ hoạt động chính xác nếu bạn đang sử dụng chức năng "ma thuật"
function updateNestedState[state, action] {
let nestedState = state.nestedState
// ERROR: this directly modifies the existing object reference - don't do this!
nestedState.nestedField = action.data
return {
...state,
nestedState
}
}
8 từ Bộ công cụ Redux bao bọc bộ giảm tốc này trong chức năng function updateNestedState[state, action] {
// Problem: this only does a shallow copy!
let newState = { ...state }
// ERROR: nestedState is still the same object!
newState.nestedState.nestedField = action.data
return newState
}
1 của Immer. Nếu bộ giảm tốc này được sử dụng mà không có Immer, nó sẽ thực sự thay đổi trạng thái. Cũng không rõ ràng chỉ bằng cách nhìn vào mã mà chức năng này thực sự an toàn và cập nhật trạng thái một cách bất biến. Vui lòng đảm bảo rằng bạn hiểu đầy đủ các khái niệm về cập nhật bất biến. Nếu bạn sử dụng điều này, có thể hữu ích khi thêm một số nhận xét vào mã của bạn để giải thích các bộ giảm tốc của bạn đang sử dụng Bộ công cụ Redux và ImmerNgoài ra, tiện ích
function updateNestedState[state, action] {
// Problem: this only does a shallow copy!
let newState = { ...state }
// ERROR: nestedState is still the same object!
newState.nestedState.nestedField = action.data
return newState
}
2 của Redux Toolkit sẽ tự động tạo các trình tạo hành động và các loại hành động dựa trên các hàm rút gọn mà bạn cung cấp, với cùng khả năng cập nhật do Immer cung cấp bên trong