Skip to content

Instantly share code, notes, and snippets.

@browniefed
Created March 9, 2018 17:22
Show Gist options
  • Save browniefed/80e70289983e67fcfc0bf9fd42b21429 to your computer and use it in GitHub Desktop.
Save browniefed/80e70289983e67fcfc0bf9fd42b21429 to your computer and use it in GitHub Desktop.

Setup

First off, check out the Live Demo so you know what we're building.

Our basic setup is a simple incrementer/decrementer. We have a value in state, and 2 buttons to control that value.

When those buttons are pressed we call either increment or decrement.

https://gist.github.com/e27529c0d6e4c1735b207a3d2dcca150

The Wrong Way

Here is how many would do this.

https://gist.github.com/e9ac41264d2d5cb383015f408cd1389b

There is theoretically nothing wrong with this at the moment. However once we hit the async world of React where updates don't happen exactly when setState is called there is a potential for this to cause issues with your app.

Yes incrementing a number is trivial but apply it to anytime you've used setState.

If you were to call setState twice in a row and reference this.state.value. The value on this.state has not been updated yet.

https://gist.github.com/2717640fad5eda4c52156b0c3a9ea7c0

What this would boil down to is

https://gist.github.com/e762f98b07628c5d5a3eafc83642df93

You would have wanted to update to 2 but you're only updating to 1. In order to fix this we need to use setState updater functions.

setState Updater Function

A setState updater function is a function passed to setState. It is called with both state and props. The state that it is given is the fully flushed through state from any previous setState calls.

https://gist.github.com/283ff6c8a3b93cc4b14883c5da505cbf

Lets take a look at the example up above using the setState updater pattern.

If we did it this way we would get the expected value to be 2. Using an updater function will preserve the order of how state should be applied as well as make sure all previous states are flushed through.

https://gist.github.com/a031218699ea83293a57d30b15a17067

The result is 2 rather than 1 like it was previously. So now that we have a small fundamental understanding of updater functions lets dive into turning it into a reducer pattern.

Create Constants

First off at the top of our file we create some constants that will be our actions.

https://gist.github.com/34ef459d91f6c97946a39465acb5d763

Create a Reducer

Next we setup a reducer. This is slightly different than a typical redux reducer. Look closely at the structure. It is a function that returns another function!

https://gist.github.com/62b0d2ff50b6f38c1293ff98f0f7d5d2

Here we have a function that takes an argument action. When that is called the action is in scope of the next function that is returned. This is going to be the function that is passed into setState. This is our updater function.

A typical redux reducer would have a signature like below, but in order to work with setState it needs to be adjusted slightly.

https://gist.github.com/3d5c411602a0f7ee0eaa181f2c3c5d66

Add Reducer Logic

Now we can add our logic just like any old reducer. We'll do a switch on our action.type.

https://gist.github.com/42fcb6189512b8db2bb0e82192b6b5a9

How this varies from a redux reducer is that we don't need to return an object with the state spread into it. A typical return value might look something like this.

https://gist.github.com/c44b5e8c3dac6e842052448ac740b22f

Since we are working with React and setState whatever we return will be merged into the previous state just like doing a normal setState call with just an object. So we don't need to return everything.

Finally on handy feature of setState and the updater function is if null is returned then no re-render will happen. So essentially if our reducer is called with something we don't recognize then we won't do anything and we'll tell React we don't want anything to happen either.

Setup Reducer Updater Functions

To use the reducer function along with React we need to call it with one of the constants we setup, and then pass it into setState.

https://gist.github.com/73a23e77a29a9f48dc77e8c0729370ac

If we deconstruct this a little. The reducer is called with our action. Which then returns an updater function. Then that is passed into setState just like a normal updater function.

https://gist.github.com/5deae9068e9fa0bb840a133de52a6012

Pass in Data

We can pass more than just type we can pass other data too. So if we wanted our increment to increment by 2 rather than 1 we need to add an amount to the object.

https://gist.github.com/76edc2edeb7cec905fe90f885f39cf40

Then adjust our reducer to reference our action.amount.

https://gist.github.com/13c9dd472816b088a97f608a25509c45

Ending

Why we would want to do this? Well it externalizes your state management into a testable function. It also sets us up for proper state updates when dealing with async rendering in React. You don't have to go full reducer pattern, but moving your updates to their own functions can make testing your data updating logic much easier.

Live Demo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment