useReducer Hook
useReducer Hook — բարդ state-ի կառավարում React-ում
React-ի useReducer hook-ը նախատեսված է այն դեպքերի համար, երբ state-ը բարդ է, ունի բազմաթիվ ենթատիրույթներ կամ գործողություններ։ Այն հիշեցնում է Redux-ի գաղափարը, բայց ավելի պարզ տարբերակ է՝ առանց լրացուցիչ գրադարանների։
Եթե useState բավարար է պարզ արժեքների կամ փոքր օբյեկտների համար, useReducer հարմար է այն ժամանակ, երբ ունենք բազմակի գործողություններ, որոնք փոխում են նույն state-ը տարբեր ձևերով։
const [state, dispatch] = useReducer(reducer, initialState);
Սկսենք ամենապարզ օրինակով՝ հաշվիչ (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։
useReducer-ի ուժը նրանում է, որ ամբողջ state փոփոխման տրամաբանությունը պահում ես մեկ վայրում՝ reducer-ում։ Այդպես project-ը դառնում է կանխատեսելի և հեշտ debug-ելի։
Օրինակ՝ պատկերացրու, որ ունես մի քանի կոճակ՝ որոնք պետք է տարբեր կերպ փոխեն state-ը։ useState-ի դեպքում այդ տրամաբանությունը կթաքցնվի տարբեր ֆունկցիաների մեջ։ useReducer-ի դեպքում՝ ամեն ինչ կենտրոնացված է մեկ reducer-ում։
Ահա պրոֆեսիոնալ օրինակ՝ 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-ի ներսում։
| useState | useReducer |
|---|---|
| Պարզ state (օր. input value, counter) | Բարդ state (օր. form data, lists, nested objects) |
| Փոփոխությունները տարբեր ֆունկցիաներում են | Փոփոխությունները մեկ 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, որը կառավարում է հետևյալը․
Օգտագործիր immutable updates և action.payload համակարգը։