重点知识点就是使用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;