Что делают хуки useSelector и useDispatch в Redux
Что проверяют
Понимаете ли вы, как компонент подписывается на стор и почему он перерисовывается. Знать, что useSelector читает, а useDispatch отправляет, мало: следом почти всегда спрашивают, почему компонент рендерится на каждое действие и как это исправить.
Как отвечать
useSelector(selector, equalityFn?): подписывает компонент на стор и возвращает результат селектора. После каждогоdispatchселектор запускается заново, а результат сравнивается с прошлым по строгому===. Если ссылка изменилась, компонент перерисуется;useDispatch(): возвращает функциюdispatchстора. Ссылка стабильна, пока не сменился стор, поэтому её спокойно кладут в массив зависимостейuseEffectиuseCallback;Provider: оба хука работают только внутри<Provider store={store}>, иначе будет ошибка про отсутствующий контекст.
Главное про производительность: селектор, который возвращает новый объект или массив (state => ({ a: state.a, b: state.b }), state.items.filter(...)), ломает сравнение и вызывает рендер на любое действие в приложении. Лечится тремя способами: выбирать примитивы отдельными вызовами, передать вторым аргументом shallowEqual из react-redux или мемоизировать селектор через createSelector (Reselect, он же реэкспортируется из Redux Toolkit). В режиме разработки react-redux сам предупреждает, если селектор на одинаковом входе отдаёт разные ссылки.
С Redux Toolkit отправляют не литералы, а экшены из createSlice: dispatch(todoAdded(text)). Для createAsyncThunk вызов dispatch возвращает промис, а .unwrap() даёт результат или бросает ошибку. В TypeScript заводят типизированные обёртки: useSelector.withTypes<RootState>() и useDispatch.withTypes<AppDispatch>().
Где спотыкаются
Возвращают из селектора свежий объект и удивляются лишним рендерам. Хук работает правильно: ссылка каждый раз новая.
Считают, что useSelector мемоизирует компонент, как connect. Не мемоизирует: если перерисовался родитель, перерисуется и потомок, нужен React.memo.
Пытаются вызвать хуки в классовом компоненте или внутри условия. Для классов остаётся connect, а хуки подчиняются обычным правилам React.