it-swarm.com.ru

Правильный способ обновления состояния в редукционных редукторах

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

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

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

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

Но он использовал простой массив постов. Это не нужно, чтобы найти правильный пост по идентификатору.

Вот мой код:

  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'}));
14
Max Vorozhcov

С массивами

Если вы предпочитаете использовать массивы, вы можете написать редуктор, который будет работать только с отдельными объектами 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);
}

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

С объектами

Если у каждого элемента есть уникальный идентификатор строки/числа, вы можете перевернуть ваш массив и использовать вместо него 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
    })
  });
24
Dan Prince

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

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

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

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

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