Редуктор (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()
из вне вместо коллбэка.