Đây có phải là cách chính xác để xóa một mục bằng redux không?


116

Tôi biết tôi không được phép thay đổi đầu vào và nên sao chép đối tượng để thay đổi nó. Tôi đã tuân theo quy ước được sử dụng trong một dự án redux starter sử dụng:

ADD_ITEM: (state, action) => ({
  ...state,
  items: [...state.items, action.payload.value],
  lastUpdated: action.payload.date
})

để thêm một mục - tôi sử dụng spread để nối mục đó trong mảng.

để xóa tôi đã sử dụng:

DELETE_ITEM: (state, action) => ({
  ...state,
  items: [...state.items.splice(0, action.payload), ...state.items.splice(1)],
  lastUpdated: Date.now() 
})

nhưng điều này đang làm thay đổi đối tượng trạng thái đầu vào - điều này có bị cấm mặc dù tôi đang trả về một đối tượng mới không?


1
Câu hỏi nhanh. Splice trả về các mục mà bạn đã xóa. Đó có phải là chủ ý của bạn? Nếu không, bạn nên sử dụng slice, nó cũng tuân theo luật không có đột biến.
m0meni

Trong ví dụ này, tôi đang nối hai phần của mảng với nhau thành một mảng mới - với mục tôi muốn bỏ đi. Slice cũng trả lại mục đã loại bỏ phải không? Chỉ nó làm như vậy mà không làm thay đổi mảng ban đầu để đó sẽ là cách tiếp cận tốt hơn?
CWright

@ AR7 theo gợi ý của bạn: items: [...state.items.slice(0, action.payload.value), ...state.items.slice(action.payload.value + 1 )]sử dụng Slice ngay bây giờ thay vì splice để không làm thay đổi đầu vào - đây là cách để thực hiện hay có cách nào ngắn gọn hơn?
CWright

Câu trả lời:


207

Không. Không bao giờ thay đổi trạng thái của bạn.

Mặc dù bạn đang trả lại một đối tượng mới, nhưng bạn vẫn làm ô nhiễm đối tượng cũ, điều mà bạn không bao giờ muốn làm. Điều này làm cho nó có vấn đề khi thực hiện so sánh giữa trạng thái cũ và mới. Ví dụ, trong shouldComponentUpdateđó react-redux sử dụng ẩn. Nó cũng làm cho việc du hành thời gian không thể (tức là hoàn tác và làm lại).

Thay vào đó, hãy sử dụng các phương pháp bất biến. Luôn luôn sử dụng Array#slicevà không bao giờ Array#splice.

Tôi giả sử từ mã của bạn đó action.payloadlà chỉ mục của mặt hàng đang bị xóa. Một cách tốt hơn sẽ như sau:

items: [
    ...state.items.slice(0, action.payload),
    ...state.items.slice(action.payload + 1)
],

điều này không có tác dụng nếu chúng tôi đang làm việc với các yếu tố cuối cùng trong mảng đó, cũng sử dụng ...trong báo cáo thứ hai sẽ tăng gấp đôi nội dung của tiểu bang của bạn
Thaenor

4
Hãy chứng minh điều đó bằng một ví dụ jsfiddle / codepen. Đoạn mã arr.slice(arr.length)phải luôn tạo ra một mảng trống bất kể nội dung của nó là gì arr.
David L. Walsh

1
@ david-l-walsh xin lỗi vì sự nhầm lẫn, tôi chắc đã mắc lỗi đánh máy hoặc gì đó khi kiểm tra ví dụ này. Nó hoạt động kỳ diệu. Câu hỏi duy nhất của tôi là: tại sao nhu cầu của các nhà điều hành lây lan ...trong phần thứ hai -...state.items.slice(action.payload + 1)
Thaenor

5
Array#slicetrả về một mảng. Để kết hợp hai lát cắt thành một mảng duy nhất, tôi đã sử dụng toán tử spread. Nếu không có nó, bạn sẽ có một mảng các mảng.
David L. Walsh

4
điều đó có ý nghĩa. Cảm ơn bạn rất nhiều vì đã làm rõ (và xin lỗi vì sự nhầm lẫn lúc đầu).
Thaenor 22/09/17

149

Bạn có thể sử dụng phương pháp lọc mảng để xóa một phần tử cụ thể khỏi mảng mà không làm thay đổi trạng thái ban đầu.

return state.filter(element => element !== action.payload);

Trong ngữ cảnh mã của bạn, nó sẽ trông giống như sau:

DELETE_ITEM: (state, action) => ({
  ...state,
  items: state.items.filter(item => item !== action.payload),
  lastUpdated: Date.now() 
})

1
Bộ lọc có tạo ra một mảng mới không?
chenop 19/1017

6
@chenop Có, phương thức Array.filter trả về một mảng mới. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Steph M

4
Lưu ý rằng nếu có trùng lặp, điều này sẽ xóa TẤT CẢ chúng. Để sử dụng bộ lọc để loại bỏ một chỉ số cụ thể, bạn có thể sử dụng ví dụarr.filter((val, i) => i !== action.payload )
erich2k8

21

Array.prototype.filterPhương thức ES6 trả về một mảng mới với các mục phù hợp với tiêu chí. Do đó, trong ngữ cảnh của câu hỏi ban đầu, đây sẽ là:

DELETE_ITEM: (state, action) => ({
  ...state,
  items: state.items.filter(item => action.payload !== item),
  lastUpdated: Date.now() 
})

.filter()không phải là một phương thức ES2015, nhưng đã được thêm vào trong phiên bản ES5 trước đó.
morkro

7

Một biến thể khác của trình giảm thiểu "DELETED" bất biến cho mảng có các đối tượng:

const index = state.map(item => item.name).indexOf(action.name);
const stateTemp = [
  ...state.slice(0, index),
  ...state.slice(index + 1)
];
return stateTemp;

0

Quy tắc vàng là chúng ta không trả lại trạng thái bị đột biến, mà là trạng thái mới. Tùy thuộc vào loại hành động của bạn, bạn có thể cần cập nhật cây trạng thái của mình ở nhiều dạng khác nhau khi nó chạm vào bộ giảm tốc.

Trong trường hợp này, chúng tôi đang cố gắng xóa một mục khỏi thuộc tính trạng thái.

Điều này đưa chúng ta đến khái niệm về các mẫu cập nhật bất biến (hoặc sửa đổi dữ liệu) của Redux. Tính bất biến là chìa khóa vì chúng ta không bao giờ muốn thay đổi trực tiếp một giá trị trong cây trạng thái, mà là luôn tạo một bản sao và trả về một giá trị mới dựa trên giá trị cũ.

Dưới đây là một ví dụ về cách xóa một đối tượng lồng nhau:

// ducks/outfits (Parent)

// types
export const NAME = `@outfitsData`;
export const REMOVE_FILTER = `${NAME}/REMOVE_FILTER`;

// initialization
const initialState = {
  isInitiallyLoaded: false,
  outfits: ['Outfit.1', 'Outfit.2'],
  filters: {
    brand: [],
    colour: [],
  },
  error: '',
};

// action creators
export function removeFilter({ field, index }) {
  return {
    type: REMOVE_FILTER,
    field,
    index,
  };
}

export default function reducer(state = initialState, action = {}) {
  sswitch (action.type) {  
  case REMOVE_FILTER:
  return {
    ...state,
    filters: {
    ...state.filters,
       [action.field]: [...state.filters[action.field]]
       .filter((x, index) => index !== action.index)
    },
  };
  default:
     return state;
  }
}

Để hiểu rõ hơn về vấn đề này, hãy nhớ xem bài viết này: https://medium.com/better-programming/deleting-an-item-in-a-nested-redux-state-3de0cb3943da

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.