2016-02-12 2 views
9

Это как пример моего состояния выглядит:Заменить элемент массива с другой без мутирует состояния

const INITIAL_STATE = { 
contents: [ {}, {}, {}, etc.. ], 
meta: {} 
} 

мне нужно быть в состоянии и каким-то образом заменить элемент внутри содержимого массива, зная его индекс, я попытался:

 return { 
     ...state, 
     contents: [ 
      ...state.contents[action.meta.index], 
      { 
      content_type: 7, 
      content_body: { 
       album_artwork_url: action.payload.data.album.images[1].url, 
       preview_url: action.payload.data.preview_url, 
       title: action.payload.data.name, 
       subtitle: action.payload.data.artists[0].name, 
       spotify_link: action.payload.data.external_urls.spotify 
      } 
      } 
     ] 
     } 

где action.meta.index является индексом элемента массива, я хочу, чтобы заменить другое содержание объект, но я считаю, что это просто заменяет весь массив этого одного объекта я передаю. Я также думал об использовании .splice(), но это просто изменило бы массив?

+1

Кроме того, взглянуть на РЕАКТ неизменности хелперов - https://facebook.github.io/ react/docs/update.html Они не так просты в использовании, как они выглядят, и действительно упрощают чтение кода. – Andreyco

ответ

4

Splice мутировать массив, который необходимо использовать Slice. И вам также понадобится concat нарезанный кусок.

return Object.assign({}, state, { 
     contents: 
      state.contents.slice(0,action.meta.index) 
      .concat([{ 
      content_type: 7, 
      content_body: { 
       album_artwork_url: action.payload.data.album.images[1].url, 
       preview_url: action.payload.data.preview_url, 
       title: action.payload.data.name, 
       subtitle: action.payload.data.artists[0].name, 
       spotify_link: action.payload.data.external_urls.spotify 
      } 
      }]) 
      .concat(state.contents.slice(action.meta.index + 1)) 
    } 
+0

Отредактировано. Думаю, вы пытаетесь отправить все состояние с измененным значением содержимого. Таким образом, используйте Object.assign для отправки нового состояния, и если в Object.assign есть несколько источников для одного и того же ключа, последний источник выигрывает. – sapy

8

Просто для того, чтобы основываться на ответе @ sapy, который является правильным. Я хотел показать вам еще один пример того, как изменить свойство объекта внутри массива в Redux без изменения состояния.

У меня был массив orders в моем штате. Каждый order - это объект, содержащий множество свойств и значений. Однако я хотел изменить свойство note. Так что некоторые вещи, как этот

let orders = [order1_Obj, order2_obj, order3_obj, order4_obj]; 

, где, например order3_obj = {note: '', total: 50.50, items: 4, deliverDate: '07/26/2016'};

Так что в моем Reducer, я имел следующий код:

return Object.assign({}, state, 
{ 
    orders: 
    state.orders.slice(0, action.index) 
    .concat([{ 
     ...state.orders[action.index], 
     notes: action.notes 
    }]) 
    .concat(state.orders.slice(action.index + 1)) 
    }) 

Итак, по существу, вы делаете следующее:

1) Вырежьте массив до order3_obj так [order1_Obj, order2_obj]

2) Concat (то есть добавить в) отредактированный order3_obj с помощью трех точек ... распространения оператора и особое свойство, которое вы хотите изменить (т.е. note)

3) Concat в остальной части массива заказов с использованием .concat и .slice в конце .concat(state.orders.slice(action.index + 1)), который находится после order3_obj (в этом случае order4_obj - единственный слева).

+2

Огромное вам спасибо! Очень полезное объяснение – Alastair

+0

это очень помогло мне. ty для этого – user1189352

5

Обратите внимание, что Array.prototype.map() (docs) делает не мутировать исходный массив, так что дает еще один вариант:

const INITIAL_STATE = { 
    contents: [ {}, {}, {}, etc.. ], 
    meta: {} 
} 

// Assuming this action object design 
{ 
    type: MY_ACTION, 
    data: { 
    // new content to replace 
    }, 
    meta: { 
    index: /* the array index in state */, 
    } 
} 

function myReducer(state = INITIAL_STATE, action) { 
    switch (action.type) { 
    case MY_ACTION: 
     return { 
     ...state, 
     // optional 2nd arg in callback is the array index 
     contents: state.contents.map((content, index) => { 
      if (index === action.meta.index) { 
      return action.data 
      } 

      return content 
     }) 
     } 
    } 
} 
+1

Определенно самое изящное решение. –