One way NOT to abuse Redux

Datetime:2016-08-23 00:11:50          Topic:          Share

I took on a new client project recently, which is an existing app with sophisticated UI.

Redux sucks and fluxes are better

— One person who worked on this codebase previously

The guy didn’t expand on the why , though, and so, I’ve mostly forgotten about his frustration.

Until I got there myself.

There was something, should I say, interesting , about how they used Redux.

To illustrate the issue, here are a few simplified source snippets:

// src/containers/Main.js
@asyncConnect(
  [promise],
  ({ currentUser, locale, location }) => ({ currentUser, locale, location }),
  {
    onSignUp, onSignIn, onSignOut, onLocaleChange, markAsRead,
  }
)
class Main extends Component {
  render() {
    return <div>
      <Header
        onSignUp={this.props.onSignUp}
        onSignIn={this.props.onSignIn}
        onSignOut={this.props.onSignOut}
        onLocaleChange={this.props.onLocaleChange}
        markAsRead={this.props.markAsRead}
        currentUser={this.props.currentUser}
        locale={this.props.locale}
        location={this.props.location}
      />
      *something*
    </div>
  }
}

// src/containers/Another.js
@asyncConnect(
  [promise],
  ({ currentUser, locale, location }) => ({ currentUser, locale, location }),
  {
    onSignUp, onSignIn, onSignOut, onLocaleChange, markAsRead,
  }
)
class Another extends Component {
  render() {
    return <div>
      <Header
        onSignUp={this.props.onSignUp}
        onSignIn={this.props.onSignIn}
        onSignOut={this.props.onSignOut}
        onLocaleChange={this.props.onLocaleChange}
        markAsRead={this.props.markAsRead}
        currentUser={this.props.currentUser}
        locale={this.props.locale}
        location={this.props.location}
      />
      *something*
    </div>
  }
}

// & more like it

Well, is his claim correct? Does Redux suck?

Of course no.

If this code makes anything obvious, it’s how the Header component is dumb. It requires its consumers to prep all the data and callback it needs, from the state.

Should the Main and Another components even care about how the app header renders? What if we needed to render something additional in the header?

Would we have to update every one of its consumers???

That does seem like a misinterpretation of the container component pattern.

It is not something inherent to Redux per se. Rather, it’s something very specific to this particular codebase.

In that codebase, every route handler was a smart component, and everything else was dumb.

However, in most applications, the route handler won’t be the only smart component — there will be more.

Code like this could greatly benefit by creating a container for the header:

// src/containers/Main.js
@asyncConnect(
  [promise],
  ({ stateWeActuallyNeed }) => ({ stateWeActuallyNeed }),
  { actionsWeActuallyNeed }
)
class Main extends Component {
  render() {
    return <div>
      <Header />
      *something*
    </div>
  }
}

// src/containers/Another.js
@asyncConnect(
  [promise],
  ({ stateWeActuallyNeed }) => ({ stateWeActuallyNeed }),
  { actionsWeActuallyNeed }
)
class Another extends Component {
  render() {
    return <div>
      <Header />
      *something*
    </div>
  }
}

Now that is better!

What’s the lesson here?

If you find yourself using the same dumb component a lot, with the same props (that are derived from the state), pause for a moment.

Could creating a container component for it change things for the better? Chances are, it could.

(Also don’t blame Redux for how you use it.)