React

3 причины использовать useReducer() вместо useState()

3 причины использовать useReducer() вместо useState()

Что это?

useReducer() – это метод из React Hooks API, похожий на useState, но дающий больший контроль над управлением состояния. Он принимает функцию редюсер и начальное состояние в качестве аргументов, а возвращает состояние и метод dispatch:

const [state, dispatch] = React.useReducer(reducerFn, initialState, initFn);

Редюсер (так называется из-за типа функции, который передается методу array - Array.prototype.reduce(reducer, initialValue)) это шаблон взятый из Redux. Если вы не знакомы с Redux’ом, вкратце, редюсер представляет собой функцию, принимающую предыдущее состояние и действие в качестве аргументов, а возвращающую следующее состояние.

(prevState, action) => newState

Действия представляют собой кусочек информации который описывает что произойдет. И основываясь на этой информации, редюсер определяет как должно изменяться состояние. Действия передаются через метод dispatch(action).

3 причины его использовать

Большую часть времени вам отлично подойдет просто метод useState(), который базируется на useReducer(). Но есть случаи, когда использовать useReducer() все же предпочтительнее.

Следующее состояние зависит от предыдущего

Лучше использовать этот метод когда следующее состояние зависит от предыдущего. Это даст предсказуемость в изменении состояния. Вот простой пример:

function reducer(state, action) {
  switch (action.type) {
    case 'ADD': return { count: state.count + 1 };
    case 'SUB': return { count: state.count - 1 };
    default: return state;
  }
}

function Counter() {
  const [state, dispatch] = React.useReducer(reducer, { count: 0 });
  return (
    <>
      Итого: {state.count}
      <button onClick={() => dispatch({type: 'ADD'})}>Добавить</button>
      <button onClick={() => dispatch({type: 'SUB'})}>Вычесть</button>
    </>
  );
}

Сложные состояния

Когда состояние состоит не из примитивных значений, а является вложенным массивом или объектом. Например:

const [state, dispatch] = React.useReducer(
  fetchUsersReducer,
  {
    users: [
      { name: 'Иван', subscribred: false },
      { name: 'Маша', subscribred: true },
    ],
    loading: false,
    error: false,
  },
);

Таким состоянием легче управлять, потому что параметры зависят друг от друга и всю логику можно поместить в один редюсер.

Простота тестирования

Редюсеры это чистые функции, значит у них нет побочных эффектов. Они возвращают одно и то же значение, если задать одни и те же аргументы. Проще их тестировать, так как они не зависят от React’а. Давайте возьмем редюсер из примера со счетчиком и протестируем его:

test("должен увеличивать счетчик на один", () => {
  const newState = reducer({ count: 0 }, { type: "ADD" });
  expect(newState.count).toBe(1)
})

Заключение

useReducer() это альтернатива useState() которая дает больший контроль над управлением состояния и упрощает тестирование. Все примеры можно реализовать и с помощью useState(), поэтому в заключении, используйте тот метод который вам будет удобнее.

Сергей Монин

Сергей Монин

Я фриланствующий веб-разработчик, живу в Саратове. Провожу дни выполняя заказы на дому. Хорошо разбираюсь в PHP и JavaScript. Интересуюсь аналитикой, языком программирования R, web accessibility и другими вещами.