This is going to be a mini post. Before reading this entry, I suggest you read "Reducing react boilerplate experiment" but it's not necessary to understand what's next.

One of the pains of working with redux + reselect is connecting components to store logic. e.g.:

import { getSuggestedNicknames, suggestedNicknames } from somewhere

type Props = {
    doSignup: Function,
    suggestedNicknames: Array<string>
}

const MyLogin = ({doSignup, suggestedNicknames}: Props) => {
    // logic area ...
    const onRegister = (data) => doSignup(data)
    
    // render area ...
    <>
        <Text>Suggested top five nicknames</Text>
        {suggestedNicknames.map(nick => 
            <Text>{nick}</Text>
        )}
    </>
}

export default connect(
    (state) => ({suggestedNicknames: getSuggestedNicknames(state)}),
    {doSignup}
)

As you can appreciate, for only one action creator (doSignup) and one selector (getSuggestedNicknames) we have to include a whole lot of boilerplate: import + typing and redux connect for every component. Besides, if your component grows in complexity - using more store entities - you would spend a lot of time reading type declarations, then jumping to the end to see what props are connected, and after that trying to understand what this component is actually doing. So there is a lot of time spent just understanding whats going on.

So, is there another way to deal with this? Well, yes.

import useSignup from use-signup-hook

const MyLogin = () => {
    // use new hook
    const signup = useSignup()
    // logic area ...
    const onRegister = (data) => signup.Do(data)
    
    // render area ...
    <>
        <Text>Suggested top five nicknames</Text>
        {signup.suggestedNicknames().map(nick => 
            <Text>{nick}</Text>
        )}
    </>
}

export default MyLogin // No connect here!

Now we have only one store entity (signup) you just need to read once to understand whats going on (signup.Do and signup.suggestedNicknames). So there can't be confusion about what a signup entity can do or have.

So let's see what's inside useSignup hook:

import React from 'react'
import { doSignup, getAllSuggestedNicknames, makeGetIsNickAvailable } from './signup-store'

// You can expand these type definitions with a more explicit type.
export type SignupType = {
  getAllSuggestedNicknames: Function,
  isNickNameAvailable: Function,
  doSignup: Function,
}

const useSignup = () => {
  const d = useDispatch()
  // Signup actions and selectors
  const signup: $Shape<SignupType> = {
    ...mapDispatcher(d, {Do: doSignup}),
    suggestedNicknames: () => useSelector(getAllSuggestedNicknames),
    isNickNameAvailable: makeSelector(makeGetIsNickAvailable, React.useMemo), // Just an example using make selectors
  }

  return Object.freeze(signup)
}

// Helpers - declared once in another file.
// Look a little intimidating at first look but are there only to prevent rule of hooks outside components.

// Just a closure to ensure just one instance of useDispatch is being used.
const mapDispatcher = (d: Function, obj: Object) =>
  Object.entries(obj).reduce((accum, val) => {
    const [key, fn] = val
    accum[key] = (...args) => {
      const action = fn(...args)
      return d(action)
    }
    return accum
  }, {})

// Ensure any make selector is created once per component
// See: https://react-redux.js.org/api/hooks#using-memoizing-selectors
const makeSelector = (selector, memoFunc) => (...args: any) => {
  const getSelector = memoFunc(selector, [])
  return useSelector((state) => getSelector(state, ...args))
}

export default useSignup

It looks intimidating but this hook is just embedding already existing action dispatchers and selectors to create a dictionary of key -> functions. Consider we are declaring this just once across the project and that it would not generate boilerplate.

When I did this change in an existing project there was a 37% reduction of code for every component connected to redux/store. Also, I feel more productive understanding implementations at a faster pace, so I strongly suggest you to take a look at this approach - at least for experimentation. I didn't find performance issues since we are using a dictionary of functions.

Hope you enjoyed this tip. See ya!