Как избежать дублирования в Redux?
Пишу приложение по подсчету калорий, используя React-Redux. У меня есть 4 графы на главной странице (Завтрак, Обед, Ужин и Перекус). В данный момент у меня имеется 4 редьюсера для каждого приема пищи и вся информация в файлах дублируется. Вот как сейчас выглядит архетиктура Redux:
Я понимаю, что это неверный подход, но пока не могу найти способ сократить код и сделать его менее нагруженным. Вот пример того, как расписан мой редьюсер для Завтрака. Точно такой же код в файле остальных редьюсеров, меняется только название приемов пищи 
Как я вижу решение проблемы: Возможно, стоит создать в одном редьюсере InitialState с массивами данных, вот так:
let initialState = {
breakfast: [],
lunch: [],
dinner: [],
}
но и в таком случае у меня будет много повторяющегося кода для добавления, удаления и редактирования продуктов в редьюсере. Как можно избежать этой проблемы? Надеюсь я понятно изложила свою проблему.
Ответы (1 шт):
Все зависит от того, как вы дальше будете использовать приложение. Есть два пути:
- Например, вы действительно будете по-разному обрабатывать разные приемы пищи. И у вас появятся совершенно разные данные внутри
breakfastProducts,dinnerProductsи т.д. При таком подходе дублирование кода вполне себе оправдано. Потому что это разные сущности с точки зрения бизнеса. И даже если сейчас у них поведение одинаковое, это не значит, что оно будет таким в ближайшем будущем. При таком подходе вы имеете полностью независисые сущности, которые управляются в одном месте. Единственное, что я бы предложил тогда, это не плодить множество файлов, а описывать все в файлеducks.jsПримером такого файла может послужить такой код:
export const FETCH_SOMETHING_START = 'something/FETCH_SOMETHING_START';
const FETCH_SOMETHING_SUCCESS = 'something/FETCH_SOMETHING_SUCCESS';
const FETCH_SOMETHING_REJECT = 'something/FETCH_SOMETHING_REJECT';
export const fetchUserStart = () => ({
type: FETCH_SOMETHING_START,
});
export const fetchUserSuccess = (payload = {}) => ({
type: FETCH_SOMETHING_SUCCESS,
payload,
});
export const fetchUserReject = payload => ({
type: FETCH_SOMETHING_REJECT,
payload,
});
export const initialState = {
isLoading: true,
error: null,
data: [],
};
export const somethingReducer = (state = initialState, { type, payload }) => {
switch (type) {
case FETCH_SOMETHING_START:
return { ...state, isLoading: true, error: null };
case FETCH_SOMETHING_SUCCESS:
return {
...state,
isLoading: false,
data: payload,
};
case FETCH_SOMETHING_REJECT:
return { ...state, isLoading: false, error: payload };
default:
return state;
}
};
Вся логика, связанная с редьюсером крутится в одном файле. Здесь и константы, и экшен креаторы. Более оправдано, как по мне, но слегка вкусовщина.
- Возможно, у вас действительно всегда будет одна и таже логика для продуктов завтрака, ужина и обеда. Тогда смысла хранить их в отдельных стейтах нет. Вы можете хранить просто продукты. Ваш стейт будет выглядить так:
const state = {
isLoading: false,
error: null,
products: [
{
productName: '',
fat: ''
PRODUCT_TYPE: ''
}
]
}
Обратите внимание, я специально ввел еще одно поле PRODUCT_TYPE - в котором вы можете хранить к какому типу относится продукту. К завтраку, ужину или обеду.
А потом уже взаимодействовать с селекторами (при помощи reselect), то есть фильтровать по типу примерно таким образом:
import { createSelector } from 'reselect';
const productsSelector = state => state.products; // здесь еще зависит от того, как в mainReducer вы записали.
export const breakfastProducts = createSelector(
productsSelector,
products => products.filter(item => item.PRODUCT_TYPE === 'breakfast')
)
И тогда вы полностью избежите дублирования. Но это предполагает, что и работать вы будете со всеми продуктами одинаково. Все зависит от поставленных задач.