Повторно использовать один и тот же редуктор и эпик для нескольких вызовов 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 и т. д.
Надеюсь, это поможет.