useReducer hook
useState нь нэг, хоёр хэмжигдэхүүн бүхий энгийн state-д маш тохиромжтой. Гэвч state-уудын тоо нэмэгдэж, тэдгээр нь хоорондоо уялдаатай болох тусам код нарийн төвөгтэй болдог. useReducer бол энэ асуудлыг шийддэг hook — бүх state шинэчлэлтийн логикийг нэг reducer функцэд цуглуулдаг. Redux мэддэг бол танил ойлголт байх.
useReducer үндэс
useReducer нь гурван ойлголт дээр тулгуурладаг: state (одоогийн утга), action (юу болсон), reducer (яаж өөрчлөх):
import { useReducer } from "react";
// Reducer функц — state ба action авч шинэ state буцаана
function counterReducer(state, action) {
switch (action.type) {
case "increment":
return { count: state.count + 1 };
case "decrement":
return { count: state.count - 1 };
case "reset":
return { count: 0 };
default:
return state;
}
}
function Counter() {
// useReducer(reducer, initialState)
const [state, dispatch] = useReducer(counterReducer, { count: 0 });
return (
<div>
<p>Тоо: {state.count}</p>
<button onClick={() => dispatch({ type: "increment" })}>+1</button>
<button onClick={() => dispatch({ type: "decrement" })}>-1</button>
<button onClick={() => dispatch({ type: "reset" })}>Дахилт</button>
</div>
);
}
dispatch функц нь action объект авч reducer-т дамжуулна. Reducer шинэ state буцааж, component дахин рендер болно.
useState-тэй харьцуулах
Хэзээ useReducer руу шилжих вэ? Дараах нөхцөл биелбэл:
// ❌ useState — state-уудын тоо нэмэгдэхэд хяналтаас гарна
function TodoList() {
const [todos, setTodos] = useState([]);
const [filter, setFilter] = useState("all");
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
const [editingId, setEditingId] = useState(null);
// Нэмэх үед isLoading, error, todos гурвыг хамт өөрчлөх хэрэгтэй
async function addTodo(text) {
setIsLoading(true);
setError(null);
// ...
}
}
// ✅ useReducer — бүх логик нэг газарт, тодорхой
const initialState = {
todos: [],
filter: "all",
isLoading: false,
error: null,
editingId: null,
};
function todoReducer(state, action) {
switch (action.type) {
case "FETCH_START":
return { ...state, isLoading: true, error: null };
case "FETCH_SUCCESS":
return { ...state, isLoading: false, todos: action.payload };
case "FETCH_ERROR":
return { ...state, isLoading: false, error: action.payload };
case "ADD_TODO":
return { ...state, todos: [...state.todos, action.payload] };
case "TOGGLE_TODO":
return {
...state,
todos: state.todos.map((t) =>
t.id === action.payload ? { ...t, done: !t.done } : t,
),
};
case "SET_FILTER":
return { ...state, filter: action.payload };
default:
return state;
}
}
Бодит жишээ — Todo апп
Reducer ашиглан бүрэн ажиллагаатай todo апп:
import { useReducer } from "react";
function TodoApp() {
const [state, dispatch] = useReducer(todoReducer, initialState);
const visibleTodos = state.todos.filter((todo) => {
if (state.filter === "active") return !todo.done;
if (state.filter === "done") return todo.done;
return true;
});
function handleAdd(e) {
e.preventDefault();
const text = e.target.elements.todo.value.trim();
if (!text) return;
dispatch({
type: "ADD_TODO",
payload: { id: Date.now(), text, done: false },
});
e.target.reset();
}
return (
<div>
<form onSubmit={handleAdd}>
<input name="todo" placeholder="Шинэ даалгавар..." />
<button type="submit">Нэмэх</button>
</form>
<div>
{["all", "active", "done"].map((f) => (
<button
key={f}
onClick={() => dispatch({ type: "SET_FILTER", payload: f })}
style={{ fontWeight: state.filter === f ? "bold" : "normal" }}
>
{f === "all" ? "Бүгд" : f === "active" ? "Идэвхтэй" : "Дууссан"}
</button>
))}
</div>
<ul>
{visibleTodos.map((todo) => (
<li
key={todo.id}
onClick={() => dispatch({ type: "TOGGLE_TODO", payload: todo.id })}
style={{ textDecoration: todo.done ? "line-through" : "none" }}
>
{todo.text}
</li>
))}
</ul>
</div>
);
}
Context-тэй хослуулах
useReducer + useContext хослол бол React-д Redux-н хялбарчилсан хувилбар юм:
import { createContext, useContext, useReducer } from "react";
const StoreContext = createContext(null);
export function StoreProvider({ children }) {
const [state, dispatch] = useReducer(todoReducer, initialState);
return (
<StoreContext.Provider value={{ state, dispatch }}>
{children}
</StoreContext.Provider>
);
}
export function useStore() {
return useContext(StoreContext);
}
// Дурын component-оос ашиглана
function AddButton() {
const { dispatch } = useStore();
return (
<button
onClick={() =>
dispatch({
type: "ADD_TODO",
payload: { id: 1, text: "Шинэ", done: false },
})
}
>
Нэмэх
</button>
);
}
Дараагийн хичээлд:
Custom Hook бичих аргыг судална. Давтагдах логикийг дахин ашиглагдах hook болгон гаргах нь React-н хамгийн хүчирхэг техникүүдийн нэг юм.