Evil things you do with redux — dispatch in updating lifecycle methods

Chris Tate
Jul 31, 2017 · 4 min read

In the code base I work on, we are doing an evil thing. Something that shouldn’t be done. We are making dispatch calls in React lifecycle methods such as componentWillReceiveProps, componentWillUpdate, and componentDidUpdate.

Here is one such scenario. We have two different components that are accessing two different pieces of state.

class Address extends React.Component {
render() {
return <input value={this.props.address} onChange={this.props.changeAddress}/>;
}
}
function mapStateToProps(state) {
return {
address: state.address
}
}
function mapDispatchToProps(dispatch) {
return {
changeAddress: (address) => dispatch(changeAddress(address)),
}
}
connect(mapStateToProps, mapDispatchToProps)(Address);
class Zipcode extends React.Component {
render() {
return <input value={this.props.zipcode} onChange={this.props.changeZipcode}/>;
}
}
function mapStateToProps(state) {
return {
zipcode: state.zipcode
}
}
function mapDispatchToProps(dispatch) {
return {
changeZipcode: (zipcode) => dispatch(changeZipcode(zipcode)),
}
}
connect(mapStateToProps, mapDispatchToProps)(Zipcode);

Now suppose we want to add an alert to the address component if the user enters an address that doesn’t appear to be in the zip code they enter. We’re going to store this alert data in the redux state. There are many ways we could do that. But the solution would need to check the address when either (1) the address changed or (2) the zip code changed.

Our application uses redux-saga, so these solutions are from that perspective. I am also assuming we would need to make some network call to do the actual checking. Thus we are going to need to do this work in some async middleware.

We dispatch another action when changeZipcode or changeAddress is called. For instance, the mapDispatchToProps function of the Zipcode component would change to:

function mapDispatchToProps(dispatch) {
return {
changeZipcode: (zipcode) => {
dispatch(changeZipcode(zipcode));
dispatch(checkAddress());
}
}
}

Then we have a saga waiting for the checkAddress action to do the actual check of the address against the zipcode and update some state for us to render in the Address component.

I don’t like this solution very much because both changeZipcode and changeAddress would need this exact same logic.

Maybe we just have a saga waiting for changeZipcode or changeAddress actions and do the check when those happen. Now we don’t need a checkAddress action.

Well here is how we do it. Zipcode would not change at all. Instead we add a componentWillReceiveProps on Address that checks whether address or zipcode changed. If so, dispatch another action to check the address.

class Address extends React.Component {
componentWillReceiveProps(nextProps) {
if (this.props.zipcode !== nextProps.zipCode || this.props.address !== nextProps.address) {
this.props.checkAddress();
}
}
render() {
return (
<div>
<p>{this.props.addressAlert}</p>
<input value={this.props.address} onChange={this.props.changeAddress}/>;
</div>
);
}
}
function mapStateToProps(state) {
return {
address: state.address,
addressAlert: state.addressAlert,
zipcode: state.zipcode,
}
}
function mapDispatchToProps(dispatch) {
return {
changeAddress: (address) => dispatch(changeAddress(address)),
checkAddress: () => dispatch(checkAddress()),
}
}
connect(mapStateToProps, mapDispatchToProps)(Address);

I felt a chill when I first saw this. I looked up and asked why many times. It felt so wrong. It makes the component know way more than it should. Why does Address care about zipcode again??

This might not seem so bad. In fact, you may be saying it’s perfectly fine. Address cares about zipcode because it needs to update an address alert when zipcode changes. But here’s the thing. Your components should not be thinking about external state transitions. That concern is in the land of redux and only there.

To illustrate my point a bit more here are some potential problems. For instance, you may have a very hot piece of state that when changed, a lot of your application needs to change. You would need to add that piece of state to every component that cares about it and then write that componentWillReceiveProps check. How do you keep two components from dispatching the same action when they see that piece of state change?

Or even worse, you can have a cascade of renders.

  1. Component A does first dispatch
  2. Component B watches for change of state from dispatch in Component A and does second dispatch
  3. Component C watches for change of state from dispatch in Component B and does third dispatch
  4. And so on and so on.

Your application can go through these multi step transformations, but it’s really difficult to trace back that first action that kicked off the whole transformation.

One last thing. I’m not saying these updating lifecycle methods are bad. You absolutely need them for when your component has its own internal state. Or when you need to do some imperative DOM work when interfacing with a non react library. But they should never be for kicking off other changes to your redux state in response to other redux events.

In conclusion, make your components as simple as possible. The best component is simply a function of it’s props. And if you ever see a dispatch call in updating lifecycle methods like componentWillReceiveProps, componentWillUpdate, and componentDidUpdate there is most definitely a better way.

HackerNoon.com

#BlackLivesMatter

By HackerNoon.com

how hackers start their afternoons. the real shit is on hackernoon.com. Take a look.

By signing up, you will create a Medium account if you don’t already have one. Review our Privacy Policy for more information about our privacy practices.

Check your inbox
Medium sent you an email at to complete your subscription.

HackerNoon.com

Elijah McClain, George Floyd, Eric Garner, Breonna Taylor, Ahmaud Arbery, Michael Brown, Oscar Grant, Atatiana Jefferson, Tamir Rice, Bettie Jones, Botham Jean

Chris Tate

Written by

Software Dev with a love for all things computer, programming, fish keeping, music, and reading. Amazon. Opinions are my own.

HackerNoon.com

Elijah McClain, George Floyd, Eric Garner, Breonna Taylor, Ahmaud Arbery, Michael Brown, Oscar Grant, Atatiana Jefferson, Tamir Rice, Bettie Jones, Botham Jean

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store