Skip to content

useReducer

useReducer 是集中式的管理状态。用在复杂的数据类型上

import { useReducer } from "react";
const initState = { count: 0 };
type State = typeof initState;
type Action = { type: "increment" | "decrement" };
const reducer = (state: State, action: Action) => {
switch (action.type) {
case "increment":
return { ...state, count: state.count + 1 };
case "decrement":
return { ...state, count: state.count - 1 };
default:
return state;
}
};
const createInitialState = () => {
return {
count: 100,
};
};
function App() {
// 参数:
// 1. reducer是一个处理函数,用于更新状态,reducer中包含了两个参数,第一个参数是state,第二个参数是action。reducer会返回一个新的state
// 2. initialArg是state的初始值
// 3. init是一个可选函数,用于初始化state,如果写了init函数,初始值则使用init函数的返回值
const [state, dispatch] = useReducer(reducer, initState, createInitialState);
const handleIncrement = () => {
dispatch({ type: "increment" });
};
const handleDecrement = () => {
dispatch({ type: "decrement" });
};
return (
<>
<h2>Count: {state.count}</h2>
<button onClick={handleIncrement}>+</button>
<button onClick={handleDecrement}>-</button>
</>
);
}
export default App;
import { useMemo, useReducer } from "react";
const initState = [
{ id: 1, name: "Apple", price: 10, count: 10, isSale: false },
{ id: 2, name: "Banana", price: 20, count: 20, isSale: true },
{ id: 3, name: "Orange", price: 30, count: 30, isSale: false },
];
type State = typeof initState;
type Action =
| {
type: "ADD_COUNT" | "SUB_COUNT" | "DELETE_PRODUCT" | "EDIT_STATUS";
id: number;
}
| {
type: "UPDATE_NAME";
id: number;
newNane: string;
};
const reducer = (state: State, action: Action): State => {
switch (action.type) {
case "ADD_COUNT":
return state.map((item) =>
item.id === action.id ? { ...item, count: item.count + 1 } : item,
);
case "SUB_COUNT":
return state.map((item) =>
item.id === action.id
? { ...item, count: Math.max(0, item.count - 1) }
: item,
);
case "DELETE_PRODUCT":
return state.filter((item) => item.id !== action.id);
case "EDIT_STATUS":
return state.map((item) =>
item.id === action.id ? { ...item, isSale: !item.isSale } : item,
);
case "UPDATE_NAME":
return state.map((item) =>
item.id === action.id ? { ...item, name: action.newNane } : item,
);
default:
return state;
}
};
function App() {
const [state, dispatch] = useReducer(reducer, initState);
const totalPrice = useMemo(() => {
return state.reduce((acc, item) => acc + item.price * item.count, 0);
}, [state]);
const handleUpdateName = (id: number, currentName: string) => {
const newName = window.prompt("Please enter a new name:", currentName);
if (newName && newName.trim() !== "" && newName !== currentName) {
dispatch({ type: "UPDATE_NAME", id, newNane: newName.trim() });
}
};
return (
<div style={{ padding: "20px", fontFamily: "sans-serif" }}>
<h2>Product Management</h2>
<table
style={{
width: "100%",
maxWidth: "800px",
borderCollapse: "collapse",
textAlign: "left",
boxShadow: "0 4px 8px rgba(0,0,0,0.1)",
}}
>
<thead style={{ backgroundColor: "#f4f4f4" }}>
<tr>
<th>ID</th>
<th>Name</th>
<th>Total Price</th>
<th>Count</th>
<th>Status</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{state.length === 0 ? (
<tr>
<td colSpan={6} style={{ padding: "20px", textAlign: "center" }}>
No products available.
</td>
</tr>
) : (
state.map((item) => (
<tr key={item.id} style={{ borderBottom: "1px solid #ddd" }}>
<td>{item.id}</td>
<td>
<strong>{item.name}</strong>
</td>
<td>${(item.price * item.count).toFixed(2)}</td>
<td>
<div
style={{
display: "flex",
alignItems: "center",
gap: "10px",
}}
>
<button
disabled={item.count <= 0}
onClick={() =>
dispatch({ type: "SUB_COUNT", id: item.id })
}
>
-
</button>
<span style={{ minWidth: "20px", textAlign: "center" }}>
{item.count}
</span>
<button
onClick={() =>
dispatch({ type: "ADD_COUNT", id: item.id })
}
>
+
</button>
</div>
</td>
<td>
<button
style={{
backgroundColor: item.isSale ? "#4caf50" : "#f44336",
}}
onClick={() =>
dispatch({ type: "EDIT_STATUS", id: item.id })
}
>
{item.isSale ? "Sale" : "Not Sale"}
</button>
</td>
<td>
<button
style={{ backgroundColor: "#2196f3" }}
onClick={() => handleUpdateName(item.id, item.name)}
>
Edit Name
</button>
<button
style={{ backgroundColor: "#f44336", marginLeft: "8px" }}
onClick={() =>
dispatch({
type: "DELETE_PRODUCT",
id: item.id,
})
}
>
Delete
</button>
</td>
</tr>
))
)}
</tbody>
<tfoot>
<tr>
<td colSpan={6} style={{ textAlign: "right", fontWeight: "bold" }}>
Total Value: ${totalPrice.toFixed(2)}
</td>
</tr>
</tfoot>
</table>
</div>
);
}
export default App;