【react18】怎样利用useReducer和useContext来实现一个todoList功能

[复制链接]
发表于 2025-10-27 19:35:05 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

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


实当代码


  • 项目工程布局

  • App.js文件
  1. import logo from "./logo.svg";
  2. import "./App.css";
  3. import TodoLists from "./comps/TodoLists";
  4. import AddItem from "./comps/AddItem";
  5. import todoListsContext from "./comps/todolistContext";
  6. // import { useReducer } from "react";
  7. import { useImmerReducer } from "use-immer";
  8. import FooterCompo from "./comps/FooterCompo";
  9. // function todoListsReducer(state, action) {
  10. //   switch (action.type) {
  11. //     case "CHANGE_INPUT_VALUE":
  12. //       return {
  13. //         ...state,
  14. //         inputValue: action.payload,
  15. //       };
  16. //       break;
  17. //     case "ADD_ITEM":
  18. //       return { ...state, todos: [...state.todos, action.payload] };
  19. //       break;
  20. //     default:
  21. //       return state;
  22. //   }
  23. // }
  24. function todoListsReducer(state, action) {
  25.   switch (action.type) {
  26.     case "CHANGE_INPUT_VALUE":
  27.       state.inputValue = action.payload;
  28.       return state;
  29.     case "ADD_ITEM":
  30.       state.todos = [...state.todos, action.payload];
  31.       return state;
  32.     case "DONE_ITEM":
  33.       state.todos = state.todos.map((todo) => {
  34.         console.log(action, "done");
  35.         if (todo.id === action.payload.id) {
  36.           return { ...todo, completed: !action.payload.completed };
  37.         }
  38.         return todo;
  39.       });
  40.       return state;
  41.     case "DELETE_ITEM":
  42.       state.todos = state.todos.filter((todo) => todo.id !== action.payload);
  43.       return state;
  44.     default:
  45.       return state;
  46.   }
  47. }
  48. function App() {
  49.   // const [store, dispatch] = useReducer(todoListsReducer, {
  50.   //   inputValue: "",
  51.   //   todos: [],
  52.   // });
  53.   const [store, dispatch] = useImmerReducer(todoListsReducer, {
  54.     inputValue: "",
  55.     todos: [],
  56.   });
  57.   return (
  58.     <div className="App">
  59.       <h1>TodoLists</h1>
  60.       <todoListsContext.Provider value={store}>
  61.         <AddItem dispatch={dispatch} />
  62.         <TodoLists dispatch={dispatch} />
  63.         <FooterCompo />
  64.       </todoListsContext.Provider>
  65.     </div>
  66.   );
  67. }
  68. export default App;
复制代码

  • AddItem组件
  1. import { useContext } from "react";
  2. import todoListsContext from "./todolistContext";
  3. import { v4 as uuid } from "uuid";
  4. function AddItem({ dispatch }) {
  5.   const data = useContext(todoListsContext);
  6.   const handleChangeInputValue = (e) => {
  7.     dispatch({
  8.       type: "CHANGE_INPUT_VALUE",
  9.       payload: e.target.value,
  10.     });
  11.   };
  12.   const handleAddItem = () => {
  13.     if (!data.inputValue) {
  14.       return alert("Please enter a valid item");
  15.     }
  16.     dispatch({
  17.       type: "ADD_ITEM",
  18.       payload: {
  19.         id: uuid(),
  20.         title: data.inputValue,
  21.         completed: false,
  22.       },
  23.     });
  24.     dispatch({
  25.       type: "CHANGE_INPUT_VALUE",
  26.       payload: "",
  27.     });
  28.   };
  29.   return (
  30.     <div>
  31.       <input
  32.         type="text"
  33.         value={data.inputValue}
  34.         placeholder="Enter item name"
  35.         onChange={handleChangeInputValue}
  36.       ></input>
  37.       <button onClick={handleAddItem}>add list</button>
  38.     </div>
  39.   );
  40. }
  41. export default AddItem;
复制代码

  • FooterCompo组件
  1. import todoListsContext from "./todolistContext";
  2. import { useContext } from "react";
  3. import { useMemo } from "react";
  4. function FooterCompo() {
  5.   const { todos } = useContext(todoListsContext);
  6.   const completedCount = useMemo(
  7.     () => todos.filter((e) => e.completed).length,
  8.     [todos]
  9.   );
  10.   const remain = useMemo(
  11.     () => todos.length - completedCount,
  12.     [todos, completedCount]
  13.   );
  14.   return (
  15.     <div>
  16.       <span>all: {todos.length}</span> <span>completed: {completedCount}</span>{" "}
  17.       <span>todo: {remain}</span>
  18.     </div>
  19.   );
  20. }
  21. export default FooterCompo;
复制代码

  • 利用createContext来创建公共状态
  1. import { createContext } from "react";
  2. const todoListsContext = createContext({});
  3. export default todoListsContext;
复制代码

  • UI美化
  1. .completed {
  2.     text-decoration: line-through;
  3.     color: gray;
  4. }
  5. .todo-list{
  6.   cursor: pointer;
  7. }
  8. .todo-text{
  9.   margin: auto 10px auto 4px;
  10. }
  11. .del-btn{
  12.   cursor: pointer;
  13.   color: gray;
  14. }
  15. .todo-item{
  16.   text-align: left;
  17. }
复制代码

  • TodoLists组件
  1. import { useContext } from "react";
  2. import todoListsContext from "./todolistContext";
  3. import classNames from "classnames";
  4. import "./todoLists.css";
  5. function TodoLists({ dispatch }) {
  6.   const data = useContext(todoListsContext);
  7.   const handleDoneItem = (item) => {
  8.     dispatch({ type: "DONE_ITEM", payload: item });
  9.   };
  10.   const handleDelItem = (id) => {
  11.     dispatch({ type: "DELETE_ITEM", payload: id });
  12.   };
  13.   return (
  14.     <>
  15.       <ul>
  16.         {data.todos.map((todoList) => {
  17.           return (
  18.             <li key={todoList.id} className="todo-item">
  19.               <span
  20.                 className={classNames({
  21.                   completed: todoList.completed,
  22.                   "todo-list": true,
  23.                 })}
  24.               >
  25.                 <input
  26.                   type="checkbox"
  27.                   checked={todoList.completed}
  28.                   onChange={() => handleDoneItem(todoList)}
  29.                 />
  30.                 <span className="todo-text">{todoList.title}</span>
  31.               </span>
  32.               <span
  33.                 className={classNames({
  34.                   // completed: todoList.completed,
  35.                   "del-btn": todoList.completed,
  36.                 })}
  37.                 onClick={() => handleDelItem(todoList.id)}
  38.               >
  39.                 del
  40.               </span>
  41.             </li>
  42.           );
  43.         })}
  44.       </ul>
  45.     </>
  46.   );
  47. }
  48. export default TodoLists;
复制代码
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表