【react18】如何使用useReducer和useContext来实现一个todoList功能

重点知识点就是使用useReducer来攻坚小型的公共状态管理,useImmerReducer来实现数据的不可变

实现效果

实现代码

  • 项目工程结构
  • App.js文件
js 复制代码
import logo from "./logo.svg";
import "./App.css";
import TodoLists from "./comps/TodoLists";
import AddItem from "./comps/AddItem";
import todoListsContext from "./comps/todolistContext";
// import { useReducer } from "react";
import { useImmerReducer } from "use-immer";
import FooterCompo from "./comps/FooterCompo";

// function todoListsReducer(state, action) {
//   switch (action.type) {
//     case "CHANGE_INPUT_VALUE":
//       return {
//         ...state,
//         inputValue: action.payload,
//       };
//       break;
//     case "ADD_ITEM":
//       return { ...state, todos: [...state.todos, action.payload] };
//       break;
//     default:
//       return state;
//   }
// }
function todoListsReducer(state, action) {
  switch (action.type) {
    case "CHANGE_INPUT_VALUE":
      state.inputValue = action.payload;
      return state;

    case "ADD_ITEM":
      state.todos = [...state.todos, action.payload];
      return state;

    case "DONE_ITEM":
      state.todos = state.todos.map((todo) => {
        console.log(action, "done");
        if (todo.id === action.payload.id) {
          return { ...todo, completed: !action.payload.completed };
        }
        return todo;
      });
      return state;

    case "DELETE_ITEM":
      state.todos = state.todos.filter((todo) => todo.id !== action.payload);
      return state;

    default:
      return state;
  }
}

function App() {
  // const [store, dispatch] = useReducer(todoListsReducer, {
  //   inputValue: "",
  //   todos: [],
  // });
  const [store, dispatch] = useImmerReducer(todoListsReducer, {
    inputValue: "",
    todos: [],
  });
  return (
    <div className="App">
      <h1>TodoLists</h1>
      <todoListsContext.Provider value={store}>
        <AddItem dispatch={dispatch} />
        <TodoLists dispatch={dispatch} />
        <FooterCompo />
      </todoListsContext.Provider>
    </div>
  );
}

export default App;
  • AddItem组件
js 复制代码
import { useContext } from "react";
import todoListsContext from "./todolistContext";
import { v4 as uuid } from "uuid";

function AddItem({ dispatch }) {
  const data = useContext(todoListsContext);
  const handleChangeInputValue = (e) => {
    dispatch({
      type: "CHANGE_INPUT_VALUE",
      payload: e.target.value,
    });
  };
  const handleAddItem = () => {
    if (!data.inputValue) {
      return alert("Please enter a valid item");
    }
    dispatch({
      type: "ADD_ITEM",
      payload: {
        id: uuid(),
        title: data.inputValue,
        completed: false,
      },
    });
    dispatch({
      type: "CHANGE_INPUT_VALUE",
      payload: "",
    });
  };
  return (
    <div>
      <input
        type="text"
        value={data.inputValue}
        placeholder="Enter item name"
        onChange={handleChangeInputValue}
      ></input>
      <button onClick={handleAddItem}>add list</button>
    </div>
  );
}

export default AddItem;
  • FooterCompo组件
js 复制代码
import todoListsContext from "./todolistContext";
import { useContext } from "react";
import { useMemo } from "react";
function FooterCompo() {
  const { todos } = useContext(todoListsContext);
  const completedCount = useMemo(
    () => todos.filter((e) => e.completed).length,
    [todos]
  );
  const remain = useMemo(
    () => todos.length - completedCount,
    [todos, completedCount]
  );
  return (
    <div>
      <span>all: {todos.length}</span> <span>completed: {completedCount}</span>{" "}
      <span>todo: {remain}</span>
    </div>
  );
}

export default FooterCompo;
  • 使用createContext来创建公共状态
js 复制代码
import { createContext } from "react";

const todoListsContext = createContext({});

export default todoListsContext;
  • UI美化
css 复制代码
.completed {
    text-decoration: line-through;
    color: gray;
}
.todo-list{
  cursor: pointer;
}

.todo-text{
  margin: auto 10px auto 4px;
}
.del-btn{
  cursor: pointer;
  color: gray;
}
.todo-item{
  text-align: left;

}
  • TodoLists组件
js 复制代码
import { useContext } from "react";
import todoListsContext from "./todolistContext";
import classNames from "classnames";
import "./todoLists.css";

function TodoLists({ dispatch }) {
  const data = useContext(todoListsContext);
  const handleDoneItem = (item) => {
    dispatch({ type: "DONE_ITEM", payload: item });
  };
  const handleDelItem = (id) => {
    dispatch({ type: "DELETE_ITEM", payload: id });
  };
  return (
    <>
      <ul>
        {data.todos.map((todoList) => {
          return (
            <li key={todoList.id} className="todo-item">
              <span
                className={classNames({
                  completed: todoList.completed,
                  "todo-list": true,
                })}
              >
                <input
                  type="checkbox"
                  checked={todoList.completed}
                  onChange={() => handleDoneItem(todoList)}
                />
                <span className="todo-text">{todoList.title}</span>
              </span>
              <span
                className={classNames({
                  // completed: todoList.completed,
                  "del-btn": todoList.completed,
                })}
                onClick={() => handleDelItem(todoList.id)}
              >
                del
              </span>
            </li>
          );
        })}
      </ul>
    </>
  );
}
export default TodoLists;
相关推荐
Rowrey1 小时前
react+typescript,初始化与项目配置
javascript·react.js·typescript
谢尔登1 小时前
Vue 和 React 的异同点
前端·vue.js·react.js
化作繁星9 小时前
React 高阶组件的优缺点
前端·javascript·react.js
贩卖纯净水.14 小时前
REACT学习DAY02(恨连接不上服务器)
服务器·学习·react.js
一路向前的月光16 小时前
react(9)-redux
前端·javascript·react.js
谢尔登17 小时前
Vue 和 React 响应式的区别
前端·vue.js·react.js
程序员远仔20 小时前
【Vue.js 和 React.js 的主要区别是什么?】
前端·javascript·css·vue.js·react.js·性能优化·html5
小刘不知道叫啥1 天前
React源码揭秘 | 启动入口
前端·react.js·前端框架
程序员小续2 天前
Excel 表格和 Node.js 实现数据转换工具
前端·javascript·react.js·前端框架·vue·excel·reactjs