useReducer Hook — բարդ state-ի կառավարում React-ում
React-ի useReducer hook-ը նախատեսված է այն դեպքերի համար, երբ state-ը բարդ է, ունի բազմաթիվ ենթատիրույթներ կամ գործողություններ։ Այն հիշեցնում է Redux-ի գաղափարը, բայց ավելի պարզ տարբերակ է՝ առանց լրացուցիչ գրադարանների։
Եթե useState բավարար է պարզ արժեքների կամ փոքր օբյեկտների համար, useReducer հարմար է այն ժամանակ, երբ ունենք բազմակի գործողություններ, որոնք փոխում են նույն state-ը տարբեր ձևերով։
🔹 Սինտաքսը
const [state, dispatch] = useReducer(reducer, initialState);
- reducer — ֆունկցիա, որը որոշում է՝ ինչպես փոխել state-ը action-ի հիման վրա։
- initialState — state-ի սկզբնական արժեքը։
- dispatch — ֆունկցիա, որը կանչում ենք՝ փոփոխություն անելու համար։
📍 Օրինակ 1 — Counter հավելված useReducer-ով
Սկսենք ամենապարզ օրինակով՝ հաշվիչ (counter)։
import React, { useReducer } from 'react';
// reducer ֆունկցիա
function reducer(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;
}
}
export default function Counter() {
const [state, dispatch] = useReducer(reducer, { count: 0 });
return (
<div>
<h2>Count: {state.count}</h2>
<button onClick={() => dispatch({ type: 'increment' })}>+1</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-1</button>
<button onClick={() => dispatch({ type: 'reset' })}>Reset</button>
</div>
);
}
Այս օրինակում reducer-ը ստանում է երկու արժեք՝
state և action։
Action-ը սովորաբար ունի type և երբեմն payload։
🧠 Ինչու reducer pattern-ը ավելի հստակ է
useReducer-ի ուժը նրանում է, որ ամբողջ state փոփոխման տրամաբանությունը պահում ես մեկ վայրում՝ reducer-ում։ Այդպես project-ը դառնում է կանխատեսելի և հեշտ debug-ելի։
Օրինակ՝ պատկերացրու, որ ունես մի քանի կոճակ՝ որոնք պետք է տարբեր կերպ փոխեն state-ը։ useState-ի դեպքում այդ տրամաբանությունը կթաքցնվի տարբեր ֆունկցիաների մեջ։ useReducer-ի դեպքում՝ ամեն ինչ կենտրոնացված է մեկ reducer-ում։
📍 Օրինակ 2 — ToDo List reducer pattern-ով
Ահա պրոֆեսիոնալ օրինակ՝ task-երի կառավարում reducer-ի միջոցով։
import React, { useReducer, useState } from 'react';
function reducer(state, action) {
switch (action.type) {
case 'add':
return [...state, { id: Date.now(), text: action.payload, done: false }];
case 'toggle':
return state.map(task =>
task.id === action.payload ? { ...task, done: !task.done } : task
);
case 'delete':
return state.filter(task => task.id !== action.payload);
default:
return state;
}
}
export default function TodoApp() {
const [tasks, dispatch] = useReducer(reducer, []);
const [text, setText] = useState('');
function handleAdd() {
if (text.trim()) {
dispatch({ type: 'add', payload: text });
setText('');
}
}
return (
<div>
<h2>Աշխատանքների ցուցակ</h2>
<input
value={text}
onChange={e => setText(e.target.value)}
placeholder="Ավելացրու առաջադրանք"
/>
<button onClick={handleAdd}>Ավելացնել</button>
<ul>
{tasks.map(task => (
<li key={task.id}>
<span
style={{ textDecoration: task.done ? 'line-through' : 'none', cursor: 'pointer' }}
onClick={() => dispatch({ type: 'toggle', payload: task.id })}
>
{task.text}
</span>
<button onClick={() => dispatch({ type: 'delete', payload: task.id })}>✖</button>
</li>
))}
</ul>
</div>
);
}
Այս օրինակը ցույց է տալիս reducer pattern-ի ամբողջ հզորությունը՝ բոլոր գործողությունները (ավելացնել, ջնջել, toggle) կատարվում են մեկ հստակ կառավարվող reducer-ի ներսում։
⚡ Խորհուրդներ պրոֆեսիոնալների համար
- Reducer-ը պետք է լինի pure function՝ առանց side effects (fetch, log, localStorage և այլն)։
- Եթե state-ը բարդ օբյեկտ է, օգտագործիր immutable update pattern։
- Reducer-ը միշտ պետք է վերադարձնի նոր state օբյեկտ։
- Կառուցիր action-ները հստակ type-երով և ցանկության դեպքում՝ payload-ով։
💡 useReducer vs useState
| useState | useReducer |
|---|---|
| Պարզ state (օր. input value, counter) | Բարդ state (օր. form data, lists, nested objects) |
| Փոփոխությունները տարբեր ֆունկցիաներում են | Փոփոխությունները մեկ reducer-ի ներսում են |
| Արագ և պարզ | Կառուցվածքային և կանխատեսելի |
🧩 Բարդ օրինակ — Nested state reducer
Եթե ունես nested օբյեկտ state, reducer-ը կարող է update անել միայն անհրաժեշտ ենթադաշտերը՝ immutable ձևով։
function reducer(state, action) {
switch (action.type) {
case 'updateProfile':
return {
...state,
profile: { ...state.profile, name: action.payload }
};
case 'toggleNotifications':
return {
...state,
settings: { ...state.settings, notifications: !state.settings.notifications }
};
default:
return state;
}
}
const initialState = {
profile: { name: 'Արամ', email: 'aram@example.com' },
settings: { theme: 'light', notifications: true }
};
🎯 Վարժություն (փորձիր ինքդ)
Գրիր reducer, որը կառավարում է հետևյալը․
- Կարող է ավելացնել ապրանքներ “cart”-ի մեջ։
- Կարող է հեռացնել ապրանք cart-ից։
- Կարող է հաշվարկել ընդհանուր գինը։
Օգտագործիր immutable updates և action.payload համակարգը։
📘 Ամփոփում
- useReducer — React-ի hook բարդ state-ի կառավարման համար։
- Reducer-ը ստանում է (state, action) և վերադարձնում է նոր state։
- Reducer-ը պետք է լինի pure function՝ առանց կողմնակի էֆեկտների։
- Օգտագործիր dispatch action-ներ՝ փոփոխությունները կենտրոնացնելու համար։
- Reducer pattern-ը դարձնում է state-ի կառավարումը կանխատեսելի և հստակ։