Можно ли управлять состоянием, используя более одного редуктора с помощью хука react useReducer?
Я пытаюсь привыкнуть конкретно реагировать на хуки useReduce
, для этого я делаю todoList
пример, в котором я хотел бы использовать одно сокращение для управления состоянием для таких действий, как добавление, редактирование, обновление, удаление и изменение статуса завершения задач, а в другом уменьшении управляющих фильтров, которые позволяют мне перечислить все todos
или просто завершенное и незаконченное.
Is it possible to manage the state through more than one reducer using useReducer hook?
Right now, I have had to mix the reducers, having the following logic
import React, { useReducer, useState } from "react";
import uuid from "uuid/v4";
import Form from "../Form/Form";
import List from "../List/List";
import Filter from "../Filter/Filter";
const todoReducer = (state, action) => {
switch (action.type) {
case "ADD_TODO":
return applyAddTodo(state, action);
case "DELETE_TODO":
return applyDeleteTodo(state, action);
case "EDIT_TODO":
return applyEditTodo(state, action);
case "CANCEL_EDIT_TODO":
return applyCancelEditTodo(state, action);
case "UPDATE_TODO":
return applyUpdateTodo(state, action);
case "TOGGLE_COMPLETED_TODO":
return applyToggleCompletedTodo(state, action);
case "COMPLETED": // <- from here this should be in another reducer
return applyFilterCompleted(state);
case "UN_COMPLETED":
return applyFilterUncompleted(state);
case "ALL":
default:
return [...state];
}
};
const todoFilter = (state, action) => {
switch (action.type) {
case "COMPLETED":
return applyFilterCompleted(state, action);
case "UN_COMPLETED":
return applyFilterUncompleted(state, action);
case "ALL":
default:
return [...state];
}
};
const applyAddTodo = (state, action) => [
...state,
{ id: uuid(), task: action.payload.task, completed: false, isEditing: false }
];
const applyDeleteTodo = (state, action) =>
state.filter(todo => todo.id !== action.payload.id);
const applyEditTodo = (state, action) =>
state.map(todo =>
todo.id === action.payload.id ? { ...todo, isEditing: true } : { ...todo }
);
const applyCancelEditTodo = (state, action) =>
state.map(todo =>
todo.id === action.payload.id ? { ...todo, isEditing: false } : { ...todo }
);
const applyUpdateTodo = (state, action) =>
state.map(todo =>
todo.id === action.payload.id
? { ...todo, task: action.payload.task, isEditing: false }
: { ...todo }
);
const applyToggleCompletedTodo = (state, action) =>
state.map(todo =>
todo.id === action.payload.id
? { ...todo, completed: !todo.completed }
: { ...todo }
);
const applyFilterCompleted = state => state.filter(todo => todo.completed);
const applyFilterUncompleted = state => state.filter(todo => !todo.completed);
const INITIAL_STATE = [
{
id: uuid(),
task: "Learn react",
dateCreated: Date.now(),
completed: false,
isEditing: false
},
{
id: uuid(),
task: "Learn react hooks",
dateCreated: Date.now(),
completed: true,
isEditing: false
}
];
const App = () => {
const [state, dispatch] = useReducer(todoReducer, INITIAL_STATE);
const [filter, setFilter] = useState("ALL");
const addTodoHandler = task =>
dispatch({ type: "ADD_TODO", payload: { task } });
const editTodoHandler = id =>
dispatch({ type: "EDIT_TODO", payload: { id } });
const updateTodoHandler = (id, task) =>
dispatch({ type: "UPDATE_TODO", payload: { id, task } });
const cancelEditTodoHandler = id =>
dispatch({ type: "CANCEL_EDIT_TODO", payload: { id } });
const deleteTodoHandler = id =>
dispatch({ type: "DELETE_TODO", payload: { id } });
const toggleCompletedTodoHandler = id =>
dispatch({ type: "TOGGLE_COMPLETED_TODO", payload: { id } });
const filterHandler = filter => setFilter(filter);
return (
<div>
<Form onAddTodo={addTodoHandler} />
{state.length > 0 && (
<div>
<List
todos={todoFilter(state, { type: filter })}
onEditTodo={editTodoHandler}
onUpdateTodo={updateTodoHandler}
onCancelEditTodo={cancelEditTodoHandler}
onDeleteTodo={deleteTodoHandler}
onToggleCompletedTodo={toggleCompletedTodoHandler}
/>
<Filter onFilter={filterHandler} />
</div>
)}
</div>
);
};
export default App;
Right now the reduce todoFilter
I use it as a helper to list the todos
Update 1
I got this solution
Todo reducer
const todoReducer = (state, action) => {
switch (action.type) {
case "ADD_TODO":
return applyAddTodo(state, action);
case "DELETE_TODO":
return applyDeleteTodo(state, action);
case "EDIT_TODO":
return applyEditTodo(state, action);
case "CANCEL_EDIT_TODO":
return applyCancelEditTodo(state, action);
case "UPDATE_TODO":
return applyUpdateTodo(state, action);
case "TOGGLE_COMPLETED_TODO":
return applyToggleCompletedTodo(state, action);
default:
return state;
}
};
Filter reducer
const filterReducer = (state, action) => {
switch (action.type) {
case "COMPLETED":
return applyFilterCompleted(state, action);
case "UN_COMPLETED":
return applyFilterUncompleted(state, action);
case "ALL":
default:
return [...state];
}
};
Main reducer
const reducer = (state, action) => {
switch (action.type) {
case "ADD_TODO":
case "DELETE_TODO":
case "EDIT_TODO":
case "UPDATE_TODO":
case "CANCEL_EDIT_TODO":
case "TOGGLE_COMPLETED_TODO":
return todoReducer(state, action);
case "COMPLETED":
case "UN_COMPLETED":
case "ALL":
return filterReducer(state, action);
default:
throw new Error();
}
};
This works, but I don't like it for the reason of having to repeat the action types in the main and secondary reducers, eventually some action type will be omitted and the application will fail.
Any smarter solution?
Thanks for your comments