Figuring Out Redux Best Practices (Part 1)
I recently made a blackjack web app using React and, anticipating an application-wide need of several pieces of information from a large data store, I decided to use Redux for my state management. Dan Abramov suggests using React’s own component state mechanism when possible but says when “frustration” hits, it may be time to use a more powerful tool.
Well frustration hit for me a while back when my projects required me to ask “which great-great-great-ancestor of this component holds the piece of state I need?” So I chose Redux off the bat this time around. And things were better….and weirder.
It’s kind of like someone gave me a free state-of-the-art television but I didn’t know whether to put it on a stand or mount it on the wall — or should I get a nice credenza? DirecTV or Dish? Hmmm…
In other words, Redux is great but I wasn’t sure which patterns to implement. Everything works fine when your action creators are simple:
const resetDeck = () => ({ type: 'RESET_DECK'})
Hey no problem there! But what happens when a player wants to split a hand? I need to gather from my current state what the cards are in his hand and then run a couple of state updates that will add another hand with one of the current cards and remove said card from the first hand. I could do something like this:
const split = (handIndex) => (dispatch, getState) => {
const { cards } = getState().player.hands[handIndex]
dispatch(addHand())
dispatch(dealCard(cards[1]))
dispatch(dealCard(deck[0], 1)
dispatch(removeCard(deck[0], 0)
}
Not the best. What happened was I got so obsessed with keeping my container components so clean that I didn’t realize I was just taking that mess and shoving it in my action creators. Nobody likes the idea of passing state around more than is absolutely necessary but if that’s the price for clarity I’ll pay it. I instead chose to give my components more control so that my action creators could be given more information and move towards purity.
mapStateToProps(state) => ({
deck: state.deck,
activeHandIndex: state.player.hands.findIndex(...)
})
Now my components can bind the proper values to each dispatch. The containers can quite easily dispatch the ACs themselves if that’s your preference. I personally still like having the split() function in my actions but it’s nice not having to deal with getState() anymore. But is getState() gone for good? Ughhh I wish. I’ll save my frustrations with ‘conditional dispatches’ for next time.