Cách cập nhật giá trị đơn bên trong mục mảng cụ thể trong redux


106

Tôi gặp sự cố khi kết xuất lại trạng thái gây ra sự cố ui và được đề xuất chỉ cập nhật giá trị cụ thể bên trong trình giảm thiểu của tôi để giảm lượng hiển thị lại trên một trang.

đây là ví dụ về trạng thái của tôi

{
 name: "some name",
 subtitle: "some subtitle",
 contents: [
   {title: "some title", text: "some text"},
   {title: "some other title", text: "some other text"}
 ]
}

và tôi hiện đang cập nhật nó như thế này

case 'SOME_ACTION':
   return { ...state, contents: action.payload }

đâu action.payloadlà toàn bộ mảng chứa các giá trị mới. Nhưng bây giờ tôi thực sự chỉ cần cập nhật văn bản của mục thứ hai trong mảng nội dung và một cái gì đó như thế này không hoạt động

case 'SOME_ACTION':
   return { ...state, contents[1].text: action.payload }

nơi action.payloadbây giờ là một văn bản tôi cần để cập nhật.

Câu trả lời:


58

Bạn có thể sử dụng trình trợ giúp React Immutability

import update from 'react-addons-update';

// ...    

case 'SOME_ACTION':
  return update(state, { 
    contents: { 
      1: {
        text: {$set: action.payload}
      }
    }
  });

Mặc dù tôi sẽ tưởng tượng rằng bạn có thể sẽ làm điều gì đó giống như thế này hơn?

case 'SOME_ACTION':
  return update(state, { 
    contents: { 
      [action.id]: {
        text: {$set: action.payload}
      }
    }
  });

thực sự, tôi có cần bao gồm các trình trợ giúp này từ phản ứng bên trong trình giảm tốc của tôi bằng cách nào đó không?
Ilja

8
Mặc dù gói đã được chuyển đến kolodny/immutability-helper, vì vậy bây giờ nó yarn add immutability-helperimport update from 'immutability-helper';
SacWebDeveloper

Trường hợp của tôi cũng giống hệt như vậy, nhưng khi tôi cập nhật đối tượng ở một trong các phần tử trong mảng, giá trị được cập nhật không phản ánh trong componentWillReceiveProps. Tôi sử dụng nội dung trực tiếp trong mapStateToProps của mình, chúng tôi có phải sử dụng thứ gì khác trong mapStateToProps để nó phản ánh không?
Srishti Gupta

171

Bạn có thể sử dụng map. Đây là một ví dụ triển khai:

case 'SOME_ACTION':
   return { 
       ...state, 
       contents: state.contents.map(
           (content, i) => i === 1 ? {...content, text: action.payload}
                                   : content
       )
    }

đó là cách tôi cũng đang làm, tôi chỉ không chắc đó có phải là một cách tiếp cận tốt hay không vì bản đồ sẽ trả về một mảng mới. Có suy nghĩ gì không?
Thiago C. S Ventura

@Ventura có, nó là tốt vì nó tạo ra một tham chiếu mới cho các mảng và một đối tượng mới đơn giản cho một trong những dự định sẽ được cập nhật (và duy nhất), mà là chính xác những gì bạn muốn
ivcandela

3
Tôi nghĩ đây là cách tốt nhất
Jesus Gomez

12
Tôi cũng làm điều này thường xuyên, nhưng có vẻ tương đối tốn kém về mặt tính toán khi lặp lại tất cả các phần tử thay vì cập nhật chỉ mục cần thiết.
Ben Creasy,

một cái đẹp! Tôi thích nó !
Nate Ben,

21

Bạn không cần phải làm mọi thứ trong một dòng:

case 'SOME_ACTION':
  const newState = { ...state };
  newState.contents = 
    [
      newState.contents[0],
      {title: newState.contnets[1].title, text: action.payload}
    ];
  return newState

1
Bạn nói đúng, tôi đã sửa chữa nhanh chóng. btw, mảng có độ dài cố định không? và chỉ mục bạn muốn sửa đổi có cố định không? Cách tiếp cận hiện tại chỉ khả thi với mảng nhỏ và với chỉ mục cố định.
Yuya

2
bọc phần thân trường hợp của bạn trong {}, vì const là phạm vi khối.
Benny Powers,

13

Rất muộn cho bữa tiệc nhưng đây là một giải pháp chung hoạt động với mọi giá trị chỉ mục.

  1. Bạn tạo và trải mảng mới từ mảng cũ lên đến mảng indexbạn muốn thay đổi.

  2. Thêm dữ liệu bạn muốn.

  3. Tạo và trải mảng mới từ mảng indexbạn muốn thay đổi đến cuối mảng

let index=1;// probabbly action.payload.id
case 'SOME_ACTION':
   return { 
       ...state, 
       contents: [
          ...state.contents.slice(0,index),
          {title: "some other title", text: "some other text"},
         ...state.contents.slice(index+1)
         ]
    }

Cập nhật:

Tôi đã tạo một mô-đun nhỏ để đơn giản hóa mã, vì vậy bạn chỉ cần gọi một hàm:

case 'SOME_ACTION':
   return {
       ...state,
       contents: insertIntoArray(state.contents,index, {title: "some title", text: "some text"})
    }

Để biết thêm ví dụ, hãy xem kho lưu trữ

chữ ký hàm:

insertIntoArray(originalArray,insertionIndex,newData)

3

Tôi tin rằng khi bạn cần các loại hoạt động này trên trạng thái Redux của bạn , toán tử spread là bạn của bạn và chính điều này áp dụng cho tất cả trẻ em.

Hãy giả sử đây là trạng thái của bạn:

const state = {
    houses: {
        gryffindor: {
          points: 15
        },
        ravenclaw: {
          points: 18
        },
        hufflepuff: {
          points: 7
        },
        slytherin: {
          points: 5
        }
    }
}

Và bạn muốn thêm 3 điểm cho Ravenclaw

const key = "ravenclaw";
  return {
    ...state, // copy state
    houses: {
      ...state.houses, // copy houses
      [key]: {  // update one specific house (using Computed Property syntax)
        ...state.houses[key],  // copy that specific house's properties
        points: state.houses[key].points + 3   // update its `points` property
      }
    }
  }

Bằng cách sử dụng toán tử spread, bạn chỉ có thể cập nhật trạng thái mới, giữ nguyên mọi thứ khác.

Ví dụ được lấy từ bài báo tuyệt vời này , bạn có thể tìm thấy hầu hết mọi lựa chọn khả thi với các ví dụ tuyệt vời.


3

Trong trường hợp của tôi, tôi đã làm điều gì đó như thế này, dựa trên câu trả lời của Luis:

...State object...
userInfo = {
name: '...',
...
}

...Reducer's code...
case CHANGED_INFO:
return {
  ...state,
  userInfo: {
    ...state.userInfo,
    // I'm sending the arguments like this: changeInfo({ id: e.target.id, value: e.target.value }) and use them as below in reducer!
    [action.data.id]: action.data.value,
  },
};

0

Đây là cách tôi đã làm cho một trong những dự án của mình:

const markdownSaveActionCreator = (newMarkdownLocation, newMarkdownToSave) => ({
  type: MARKDOWN_SAVE,
  saveLocation: newMarkdownLocation,
  savedMarkdownInLocation: newMarkdownToSave  
});

const markdownSaveReducer = (state = MARKDOWN_SAVED_ARRAY_DEFAULT, action) => {
  let objTemp = {
    saveLocation: action.saveLocation, 
    savedMarkdownInLocation: action.savedMarkdownInLocation
  };

  switch(action.type) {
    case MARKDOWN_SAVE:
      return( 
        state.map(i => {
          if (i.saveLocation === objTemp.saveLocation) {
            return Object.assign({}, i, objTemp);
          }
          return i;
        })
      );
    default:
      return state;
  }
};

0

Tôi e rằng việc sử dụng map()phương thức của một mảng có thể tốn kém vì toàn bộ mảng phải được lặp lại. Thay vào đó, tôi kết hợp một mảng mới bao gồm ba phần:

  • cái đầu - các mục trước khi mục được sửa đổi
  • mặt hàng đã sửa đổi
  • tail - các mục sau mục đã sửa đổi

Đây là ví dụ mà tôi đã sử dụng trong mã của mình (NgRx, nhưng cơ chế vẫn giống nhau đối với các triển khai Redux khác):

// toggle done property: true to false, or false to true

function (state, action) {
    const todos = state.todos;
    const todoIdx = todos.findIndex(t => t.id === action.id);

    const todoObj = todos[todoIdx];
    const newTodoObj = { ...todoObj, done: !todoObj.done };

    const head = todos.slice(0, todoIdx - 1);
    const tail = todos.slice(todoIdx + 1);
    const newTodos = [...head, newTodoObj, ...tail];
}
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.