Повторно использовать один и тот же редуктор и эпик для нескольких вызовов Ajax, как фабрика API?

Использует один и тот же редуктор для обновления различных частей состояния анти-паттерна?

Как мой редуктор данных имеет GET_DATA_DONE action, обновляет state.data, а затем в другом случае вы выбираете что-то еще и вызываете GET_DATA_DONE обновлять state.somethingElse?

Или вы бы сделали что-то вроде GET_SOMETHING_DATA_DONE и так далее... множественные действия diff делают одно и то же? (Едва DRY)

reducers.js

export const reducer = (state, action) => {
  switch (action.type) {
    case actions.GET_DATA_REQUESTED:
      return { ...state, isLoading: true };
    case actions.GET_DATA_DONE:
      return { ...state, isLoading: false, data: action.payload };
    case actions.GET_DATA_FAILED:
      return { ...state, isLoading: false, isError: true }
    default:
      return state;
  }
};

actions.js

export function getDataRequested() {
  return {
    type: 'GET_DATA_REQUESTED'
  };
}

export function getDataDone(data) {
  return {
    type: 'GET_DATA_DONE',
    payload: data
  };
}

export function getDataFailed(error) {
  return {
    type: 'GET_DATA_FAILED',
    payload: error
  };
};

export function getDataEpic(action$) {
  return action$.ofType(GET_DATA_REQUESTED)
    .mergeMap(action =>
      ajax.getJSON(action.url)
        .map(response => getDataDone(response))
        .catch(error => getDataFailed(error))
    );
}

Каков лучший способ структурировать приложение так, чтобы getDataEpic действует как API-интерфейс и данные, возвращаемые из getDataDone(response) может быть передан другому редуктору для обновления части состояния на основе действия, например действия города с использованием getDataDone Редуктор отправляет другое действие, которое обновляет state.cities с ответом?

Редактировать: я сделал приложение с rx-observable & redux, вызывающее 3 разных API, но я получил много дублирующегося кода и просто не доволен решением, поэтому я хочу создать правильно сконструированное приложение

Надеюсь, я был достаточно ясен.

Большое спасибо!

1 ответ

Решение

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

Для любого значимого приложения помещение всей вашей логики обновления в одну функцию редуктора быстро станет неприемлемым.

Мы не говорим о "всех наших данных", но мы говорим обо всех данных из одного вызова API.

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

В вашем примере, если целью является создание фабрики API с помощью редукторов, мы должны предположить, что API может вернуть любое количество различных структур данных, сейчас это может быть string или int но что, если это глубоко вложенный Object? Как бы вы получили доступ к этим данным и дифференцировали их между другими данными?


Допустим, у нас есть приложение с этой структурой данных только для ошибок:

{
  "errors": {
    "byId": {
      "1": {
        "code": 500,
        "message": "There was an internal server error"
      },
      "2": {
        "code": 400,
        "message": "There was another error."
      },
      "3": {
        "code": 999,
        "message": "Wow! weird error."
      },
    },
    "all": ["1", "2", "3"]
  }
}

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

byId = (state={}, action) => {
  if (action.type === 'ADD_ERROR') {
    ...state,
    [action.id]:error_reducer(state[action.id], action)
  } else {
    return state
  }
}

error_reducer может выглядеть так

errorReducer = (state={}, action) => {
  if (action.type === 'ADD_ERROR') {
    code: action.code,
    message: action.message
  } else {
    return state
  }
}

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

Другим важным преимуществом этого при работе с реальными приложениями является то, что когда данные разделены, одно действие может обновлять состояние в МНОГИХ различных областях нашего приложения. Когда редукторы обрабатывают несколько частей состояния, эти части связанного состояния становится сложнее обновлять.


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

Сказав все это, один из возможных подходов для вашей функции AJAX - написать общее действие, которое принимает объект, содержащий ваши отправки.

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

пример объекта может выглядеть так:

{
  getDataRequested: function () {
    return {
      type: 'GET_DATA_REQUESTED'
    };
  },
  getDataFailed: function (error) {
    return {
      type: 'GET_DATA_FAILED',
      payload: error
    };
  },
  getDataDone: function (data) {
    return {
      type: 'GET_DATA_DONE',
      payload: data
    };
  }
}

затем вы можете передать этот объект обратных вызовов в вашу основную функцию AJAX вместе с конечной точкой API, типом запроса REST и т. д.

Надеюсь, это поможет.

Другие вопросы по тегам