Можно ли управлять состоянием, используя более одного редуктора с помощью хука 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

0 ответов

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