useReducer является альтернативой useState
для управления состоянием компонентов с более сложной логикой. Он
особенно полезен, когда состояние состоит из нескольких взаимозависимых
полей или когда обновления состояния должны зависеть от предыдущего
состояния.
Сигнатура хука выглядит следующим образом:
const [state, dispatch] = useReducer(reducer, initialState);
state — текущее состояние
компонента.dispatch — функция для отправки
действий (actions), которая вызывает редьюсер.reducer — функция, принимающая текущее
состояние и действие, возвращающая новое состояние.initialState — начальное значение
состояния.Редьюсер — это чистая функция, которая описывает, как состояние изменяется в ответ на действия:
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { ...state, count: state.count + 1 };
case 'decrement':
return { ...state, count: state.count - 1 };
case 'reset':
return { ...state, count: 0 };
default:
return state;
}
}
Ключевые моменты:
switch
или объект-сопоставление можно обрабатывать разнообразные события.Для сложных структур состояния удобно использовать объект или вложенные объекты:
const initialState = {
user: {
name: '',
age: null
},
theme: 'light',
notifications: []
};
Редьюсер для такой структуры может выглядеть так:
function reducer(state, action) {
switch (action.type) {
case 'updateUser':
return { ...state, user: { ...state.user, ...action.payload } };
case 'toggleTheme':
return { ...state, theme: state.theme === 'light' ? 'dark' : 'light' };
case 'addNotification':
return { ...state, notifications: [...state.notifications, action.payload] };
default:
return state;
}
}
Это позволяет управлять сложным состоянием без лишней мутации и облегчает отладку.
dispatch с полезными паттернамиdispatch({ type: 'updateUser', payload: { name: 'Alice' } });
Можно создать обертку для сокращения вызовов:
const updateUser = (dispatch, payload) => {
dispatch({ type: 'updateUser', payload });
};
updateUser(dispatch, { age: 25 });
Для выполнения нескольких обновлений состояния одновременно:
dispatch({ type: 'updateUser', payload: { name: 'Bob' } });
dispatch({ type: 'toggleTheme' });
Если начальное состояние зависит от сложных вычислений, Preact поддерживает ленивую инициализацию:
const [state, dispatch] = useReducer(reducer, initialArg, initFunction);
function initFunction(initialValue) {
return {
...initialValue,
timestamp: Date.now()
};
}
Это позволяет избежать ненужных вычислений при каждом рендере.
const initialFormState = { name: '', email: '', password: '' };
function formReducer(state, action) {
switch (action.type) {
case 'updateField':
return { ...state, [action.field]: action.value };
case 'resetForm':
return initialFormState;
default:
return state;
}
}
const [formState, dispatch] = useReducer(formReducer, initialFormState);
dispatch({ type: 'updateField', field: 'email', value: 'test@example.com' });
const initialTodos = [];
function todosReducer(state, action) {
switch (action.type) {
case 'add':
return [...state, action.payload];
case 'remove':
return state.filter(todo => todo.id !== action.payload.id);
case 'toggle':
return state.map(todo =>
todo.id === action.payload.id ? { ...todo, done: !todo.done } : todo
);
default:
return state;
}
}
const [todos, dispatch] = useReducer(todosReducer, initialTodos);
dispatch({ type: 'add', payload: { id: 1, text: 'Learn Preact', done: false } });
useReducer хорошо сочетается с useEffect
для асинхронных действий, таких как загрузка данных:
useEffect(() => {
fetch('/api/data')
.then(res => res.json())
.then(data => dispatch({ type: 'setData', payload: data }));
}, []);
Такой подход централизует изменения состояния через
dispatch, облегчая поддержку сложной логики.
useReduceruseReducer становится особенно ценным инструментом в
проектах с множественными взаимодействиями и динамическими обновлениями
состояния, где простого useState недостаточно.