'How to reuse slices in redux-toolkit and handle multiple states
My app is a diary with several pages. Each page has certain areas, each area contains some content within itself. Typically, this is either a kind of a single list, or several such lists. More specifically: The first page contains 2 areas:
- Important things. The area itself contains a todolist.
- Important events. The area contains a eventlist.
The page for the month contains a few more areas. One of the areas consists of several todolists. The problem is that multiple areas of the page(s) can use lists there. To reuse the code, I used the createSlice function.
interface IList<T> {
id: number;
items: T[];
}
interface IListState<TList extends IList<TItem>, TItem> {
list?: TList;
}
export interface ITodo {
id: number;
subject: string;
done: boolean;
}
export interface ITodoList extends IList<ITodo> {
}
interface ITodoListState extends IListState<ITodoList, ITodo> {
readonlyHeader: boolean;
}
export const todoListInitialState: ITodoListState = {
list: null,
readonlyHeader: true
};
export const createTodoSlice = (listName: string) =>
createSlice({
name: listName,
initialState: todoListInitialState,
reducers: {
setTodoList(state, action: PayloadAction<ITodoList>) {
state.list = action.payload;
},
updateList(state, action: PayloadAction<ITodoList>) {
state.list = action.payload;
},
addTodo(state, action: PayloadAction<ITodo>) {
state.list.items.push(action.payload);
},
updateTodo(state, action: PayloadAction<ITodo>) {
state.list.items = state.list.items.map((item) =>
item.id === action.payload.id ? action.payload : item
);
},
deleteTodo(state, action: PayloadAction<number>) {
state.list.items = state.list.items.filter(
(todo) => todo.id !== action.payload
);
},
},
});
export type TodoSlice = ReturnType<typeof createTodoSlice>;
export const createTodoThunks = (slice: TodoSlice) => {
const actions = slice.actions;
const listService = todoService.list;
const listItemService = todoService.items;
const setList = (list: ITodoList): AppThunk => (dispatch) => {
dispatch(actions.setTodoList(list));
};
const updateList = (list: ITodoList): AppThunk => async (dispatch) => {
await listService.update(list);
dispatch(actions.updateList(list));
};
const addOrUpdateListItem = (listItem: ITodo): AppThunk => async (
dispatch
) => {
if (!listItem) return;
if (listItem.id === 0) {
await listItemService
.create(listItem)
.then((listItemId) =>
dispatch(actions.addTodo({ ...listItem, id: listItemId }))
);
} else {
await listItemService
.update(listItem)
.then((_) => dispatch(actions.updateTodo(listItem)));
}
};
const deleteListItem = (listItemId: number): AppThunk => async (dispatch) => {
if (listItemId === 0) return;
await listItemService.delete(listItemId);
dispatch(actions.deleteTodo(listItemId));
};
return {
setList,
updateList,
addOrUpdateListItem,
deleteListItem,
};
};
reducerA = createTodoSlice('A').reducer;
reducerB = createTodoSlice('B').reducer;
Reducer for page
export const mainPageReducer = combineReducers({
somePageAreaA: reducerA,
somePageAreaB: reducerB,
});
Ok, this works. But now in another area I need to store the state of several lists there. And here I am a little confused. I will have a separate state for this and can use createEntityAdapter, but how can I centrally dispatch actions for each state? How do I, for example, correctly add an item to one of the lists?
const adapter = createEntityAdapter<ITodoListState>({
selectId: (listState) => listState.list.id,
});
const slice = createSlice({
name: "multipleTodoLists",
initialState: adapter.getInitialState(),
reducers: {
itemAddedOrUpdated(state, action) {
//how can I avoid duplicating addOrUpdateListItem logic from todoSlice?
adapter.updateOne(??)
}
}
});
About reducers, I could create something like a cache of reducers, so that when setLists are called and lists are passed there, slices would be generated for each of them and reducers would be taken. And in the itemAddedOrUpdated code, I would find this reducer in the cache and run a single list through it and then update the general state through updateOne.
But I don’t know at all how to deal with thunks. I tried to add a code that seems to work, but I don't like it at all.
export const addItem =
(listId: number, newItem: IListItem): AppThunk =>
async (dispatch, getState) => {
const slice = createTodoSlice("todoList_" + listId);
const listState = getState().multipleTodoLists.entities[listId];
const createdItemId = await listItemService.create(newItem);
const newListState = slice.reducer(
listState,
slice.actions.addItem({ ...newItem, id: createdItemId })
);
dispatch(multipleTodoListsSlice.itemAddedOrUpdated(newListState));
};
Tell me the direction in which I need to move. Or maybe redux-tookit won't help me here at all and I need to change the structure as a whole?
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
