Правильный способ обновления состояния в Redux reducers

Я новичок в синтаксисе redux и es6. Я делаю свое приложение с официальным учебником redux, и с этим пример.

ниже приведен фрагмент JS. Моя точка зрения-определить случаи REQUEST_POST_BODY и RECEIVE_POST_BODY в редукторе сообщений. Главное сложно-найти и обновить нужный объект в магазине.

Я пытаюсь использовать код из примера:

  return Object.assign({}, state, {
    [action.subreddit]: posts(state[action.subreddit], action)
  })

но он использовал простой массив постов. Не нужно искать правильный пост по id.

здесь мой код:

  const initialState = {
    items: [{id:3, title: '1984', isFetching:false}, {id:6, title: 'Mouse', isFetching:false}]
  }

  // Reducer for posts store
  export default function posts(state = initialState, action) {
    switch (action.type) {
    case REQUEST_POST_BODY:
      // here I need to set post.isFetching => true
    case RECEIVE_POST_BODY:
      // here I need to set post.isFetching => false and post.body => action.body
    default:
      return state;
    }
  }

  function requestPostBody(id) {
    return {
      type: REQUEST_POST_BODY,
      id
    };
  }

  function receivePostBody(id, body_from_server) {
    return {
      type: RECEIVE_POST_BODY,
      id,
      body: body_from_server
    };
  }

  dispatch(requestPostBody(3));
  dispatch(receivePostBody(3, {id:3, body: 'blablabla'}));

2 ответов


Массивы

если вы предпочитаете придерживаться массивов, то вы можете написать редуктор, который просто решает один post объекты.

export default function reducePost(post, action) {
  if(post.id !== action.id) return post;

  switch(action.type) {
  case REQUEST_POST_BODY:
    return Object.assign({}, post, { isFetching: true });
  case RECEIVE_POST_BODY:
    return Object.assign({}, post, { isFetching: false, body: action.body });
  default:
    return post;
}

ваш корневой редуктор станет:

export default function posts(state = initialState, action) {
  return state.map(post => reducePost(post, action);
}

мы просто запускаем наш новый редуктор над каждым сообщением в списке, чтобы вернуть обновленный массив сообщений. В этом случае уникальный идентификатор гарантирует, что будет изменен только один элемент.

С Объектом

если каждый элемент имеет уникальный строка / номер id, то вы можете перевернуть массив вокруг и использовать object вместо.

const initialState = {
  items: {
    3: {id:3, title: '1984', isFetching:false},
    6: {id:6, title: 'Mouse', isFetching:false}
  };
}

после этого вы можете упростить ваш редуктор.

switch (action.type) {
case REQUEST_POST_BODY:
  let id = action.id;
  return Object.assign({}, state, {
    [id]: Object.assign({}, state[id], { isFetching: true })
  });
case RECEIVE_POST_BODY:
  let id = action.id;
  return Object.assign({}, state, {
    [id]: Object.assign({}, state[id], {
      isFetching: false,
      body: action.body
    })
  });
default:
  return state;
}

если вы также рады экспериментировать с некоторым синтаксисом ES7, вы можете включить оператор распространения объекта с Babel и переписать вызовы Object.assign.

switch (action.type) {
case REQUEST_POST_BODY:
  let id = action.id;
  return {
    ...state
    [id]: {...state[id], isFetching: true }
  };
case RECEIVE_POST_BODY:
  let id = action.id;
  return {
    ...state,
    [id]: {
      ...state[id],
      isFetching: false,
      body: action.body
    }
  };
default:
  return state;
}

если вы не так заинтересованы в использовании синтаксиса распространения, то все еще можно сделать Object.assign немного более приемлемым.

function $set(...objects) {
  return Object.assign({}, ...objects); 
}
case RECEIVE_POST_BODY:
  let id = action.id;
  return $set(state, {
    [id]: $set(state[id], {
      isFetching: false,
      body: action.body
    })
  });

если я правильно понимаю, у вас возникли проблемы с получением конкретного сообщения, которое вы хотите.

прежде всего, наличие вашего редуктора также обновляет массив и объект в нем, затрудняет чтение и обслуживание. Я предлагаю вам посмотреть это короткое видео объяснение состава редуктора с массивами. Вы можете упростить свой код, используя описанную здесь технику.

в вашем случае, вы бы posts редуктор и post редуктор, а posts редуктор вызывает post редуктор.

Что касается поиска правильного объекта для работы, предложение Дэна Принса облегчает его. Наличие карты объектов вместо массива облегчит вам задачу. Соответствующий фрагмент кода из ответа Dan:

const initialState = {
  items: {
    3: {id:3, title: '1984', isFetching:false},
    6: {id:6, title: 'Mouse', isFetching:false}
  ];
}