Редуктор (reducer) - это функция, которая принимает текущее состояние и действие (action), и возвращает новое состояние. В React, редуктор может использоваться для управления сложным состоянием компонента, которое может включать в себя несколько свойств.
Хук useReducer() имеет синтаксис следующего вида:
const [state, dispatch] = useReducer(reducer, initialState)Здесь:
state- текущее состояние компонентаdispatch- функция, которая позволяет отправлять действия (action) в редуктор для обновления состоянияreducer- функция-редуктор, которая принимает текущее состояние и действие, и возвращает новое состояниеinitialState- начальное состояние компонента
Когда компонент рендерится в первый раз, useReducer() вызывает редуктор с начальным состоянием и возвращает текущее состояние и функцию dispatch. Когда функция dispatch вызывается с действием (action), useReducer() вызывает редуктор с текущим состоянием и действием, и возвращает новое состояние.
dispatch (функция, изменяющая состояние), использует reducer под капотом. Выглядит это примерно так:
const dispatch = (action) => {
reducer(state, action)
}Другими словами, вся логика описанная внутри reducer, будет выполнена при вызове dispatch, а единственный аргумент, который принимает dispatch и будет action.
**Как не обновлять компонент при вызове dispatch. В react 18 не работает!
Бывают ситуации, когда при определенных условиях обновлять компонент не надо. Эту логику можно обработать в редукторе (reducer), но есть нюансы.
Чтобы не обновлять компонент в reducer нужно вернуть предыдущее состояние. Но это работает только для react 17 версии! В react 18 версии компонент будет обновлен в любом случае при вызове dispatch. На текущий момент актуальная версия react 18.0.2.
const reducer = (state, action) => {
switch (action.type) {
...
default:
// в react17 компонент не будет обновлен
// в react18 компонент будет обновлен
return state;
}
};Вот пример кода, который демонстрирует, как использовать useReducer() для управления состоянием компонента:
import { useReducer } from "react"
function MyComponent() {
const initialState = { count: 0 }
function reducer(state, action) {
switch (action.type) {
case "increment":
return { count: state.count + 1 }
case "decrement":
return { count: state.count - 1 }
default:
throw new Error()
}
}
const [state, dispatch] = useReducer(reducer, initialState)
return (
<div>
<h1>Количество: {state.count}</h1>
<button onClick={() => dispatch({ type: "increment" })}>+</button>
<button onClick={() => dispatch({ type: "decrement" })}>-</button>
</div>
)
}В этом примере, useReducer() используется для управления состоянием компонента MyComponent, который включает в себя счетчик. Редуктор reducer определяет два действия: увеличение и уменьшение счетчика. Хук useReducer() используется для создания текущего состояния и функции dispatch, которая позволяет отправлять действия в редуктор для обновления состояния. Кнопки + и - используют функцию dispatch для отправки действий в редуктор и обновления состояния.
Если в коде есть несколько useState и одно состояние зависит от другого - это верный признак, что лучше использовать useReducer. Можно отойти от классического использования useReducer и использовать для создания переключателей, счетчиков, состояния инпутов и вообще всего, на что хватит фантазии.
useReducer() более предпочтителен нежели useState() когда у вас сложная логика, которая включает в себя несколько значений, или когда обновляемое состояние зависит от предыдущего. useReducer() также позволяет оптимизировать компонент, так как вы можете передавать dispatch() из вне вместо коллбэка.