bindActionCreators — маленькая утилита, решающая проблемы

bindActionCreators — маленькая утилита, решающая проблемы

В этой статье я хочу поделиться информацией, которая будет полезна всем, кто использует redux или @reduxjs/toolkit

Данные библиотеки предоставляют большое количество полезных утилит, в этой статье я расскажу об одной из них – bindActionCreators

Одна из проблем, преследующая почти всех, кто использует вышеупомянутые библиотеки — невозможность использования action(), предварительно не обернув его в dispatch(). Это порождает большое количество бессмысленного кода:

const dispatch = useDispatch() const handleReset = (id: number) => dispatch(actionReset(id))

Как использовать bindActionCreators?

Для начала разберемся с аргументами:

bindActionCreators(actionCreators, dispatch)
  • actionCreators — объект вида { actionName: ActionDefinition }
  • dispatch — соответственно dispatch из useDispatch() или store

Далее разберем два способа использования по вышеупомянутым методам получения dispatch

Хук useDispatchedActions и useDispatch

const useDispatchedActions = (actionCreators) => { const dispatch = useDispatch() return bindActionCreators(actionCreators, dispatch) }

Или в одну строку:

const useDispatchedActions = (actionCreators) => bindActionCreators(actionsCreators, useDispatch())

Также пример этого хук на TypeScript с примером типов:

type Store = typeof store type AppDispatch = typeof Store['dispatch'] type BoundAsyncThunk<Actionы extends ActionCreator<any>> = ( ...args: Parameters<Action> ) => ReturnType<ReturnType<Action>> type BoundActions<Actions extends ActionCreatorsMapObject> = { [key in keyof Actions]: Actions[key] extends AsyncThunk<any, any, any> ? BoundAsyncThunk<Actions[key]> : Actions[key] } const useDispatchedActions = <Actions extends ActionCreatorsMapObject = ActionCreatorsMapObject> ( actions: Actions, ): BoundActions<Actions> => { const dispatch = useDispatch<AppDispatch>() return bindActionCreators(actions, dispatch), [actions, dispatch] }

Собственная утилита и store. dispatch

const getDispatchedActions = (actionsCreators) => bindActionCreators(actionsCreators, store.dispatch)

Результат же — тот же самый объект, который мы передавали в аргумент с одним отличием — эти экшены мы можем использовать без необходимости оборачивать их вызов в dispatch()

Я бы рекомендовал вам использовать второй метод использования bindActionCreators по двум причинам:

  • Метод более универсален, не принуждает вас использовать его только в других хуках или компонентах
  • Если вы будете использовать хук useDispatchedActions, вам необходимо учитывать, что экшены, которые вы будете получать из хука и использовать в компонентах, eslint будет требовать добавить их в зависимости других React хуков. А это может приводить к неожиданным ошибкам (например к бесконечным вызовам useEffect)