We code the web

Functionally managing state with Redux

The Flux application design pattern is still going strong and gaining popularity. There are countless libraries around, helping you implement Flux. But lately, one has been standing out.

Redux is definitely the most simple implementation of Flux I have seen so far and it’s very functional too, actually, it’s just a bunch of functions!

If you don’t know about Flux yet, you can read my article: Flux, what and why?, or go and Google some Flux…

Functions, functions everywhere

So what is redux all about? Given, it’s an implementation of Flux. But it looks quite different (all examples will be in ES6). We are going to cover the three basic concepts: the store, reducers and actions. Let’s start with the store. So how do you create one in redux ?

import { createStore } from 'redux'

let store = createStore(reducer)

Yes, just one line (excluding the import). And this is the last store you will need, yes, only one. Your  application will have one store with one big object containing you application state. So what is that reducer all about?

function reducer(state, action) {
  //modify state
  return state
}

A reducer is a just Javascript function, nothing fancy. It takes the current state and returns the next state! Just pass it to the store, and the store will use that reducer to update it’s state. Pretty simple right? No side effects, no magic.

But what will trigger the store to update it’s state? That’s where actions come in to play, they are also just … functions!

const ADD_MONEY = 'ADD_MONEY'
function addMoney(amount) {
  return {
    type: ADD_MONEY,
    amount: amount
  }
}

Again: no side effects, no magic. This action just takes an amount of money and returns an action object. The action object is essentially a message that you send to the store  and tells the reducers what to do. The object composition is completely up to you, but following the Flux pattern, at least specify the type of the action. String constants are an easy way to specify types.

Now let’s modify our reducer to handle this action:

function reducer(state = 0, action) {
  switch(action.type) {
    case ADD_MONEY:
      return state + action.amount
    default:
      return state
  }
}

So, we start off with a default of 0 money. Then if an action is fired, we check if it is an ADD_MONEY type action. If it is, we return a new state with the money added. If it is not an ADD_MONEY type action we do nothing, we just return the old state.

Notice how we don’t modify the old state, this is a very important fact. Always return a new state, never mutate the old state. You could use a library like ImmutableJS to assure this behaviour, or use Object.assign(..) to create a new state every time.

So, how do we fire off this action? I mean, we need some money right?

store.dispatch(addMoney(1000000)) //1 million!

One line, that’s it! addMoney will return an action object of type ADD_MONEY with an amount value of 1000000. The store will pass that action to the reducer, which will determine the new state. This new state is then stored in the store and can be accessed like this:

store.getState() // => 1000000

The cycle

So to recap. There are three main concepts in redux. The store, actions and reducers. Actions trigger state changes, the store holds the state and reducers calculate the next state. Here is a simplified scheme of how the redux cycle works:

Redux Cycle

Actions are triggered by either views, other actions or events/callbacks from, for instance, the server.

Getting serious

That was pretty cool right? Not a lot of code, no magic, just simple Javascript functions. Notice how we only did one import from redux, just the createStore method. Everything else is plain Javascript. But, especially when you already know Flux, this will raise some questions like:’Only one store, how do I keep code manageble then?‘, ‘How do I wire this up to my views?‘. Well, let’s answer at least these two questions.

Let’s say our application has a bigger state then just money. Let’s say it has money and awesomeness. How do we do this? We can just create 2 reducers!

function moneyReducer(state = 0, action) {
  switch(action.type) {
    case ADD_MONEY:
      return state + action.amount
    default:
      return state
  }
}

function awesomenessReducer(state = 0, action) {
  switch(action.type) {
    case INCREASE_AWESOMENESS:
      return state + action.amount
    default:
      return state
  }
}

And then combine them like this:

function mainReducer(state = {}, action) {
  return {
    money: moneyReducer(state.money, action),
    awesomeness: awesomenessReducer(state.awesomeness, action)
  }
}

let store = createStore(mainReducer)

This will result in both reducers output being combined into one object. We could also use a helper provided by redux:

import { combineReducers, createStore } from 'redux'

let store = createStore(combineReducers({
  money: moneyReducer
  awesomeness: awesomenessReducer
}))

This has the exact same result and is completely optional, but can be more convenient.

Get state still works the same, but now it returns an object with two properties:

store.getState() // => { money: 0, awesomeness: 0 }
store.dispatch(addMoney(500000))
store.getState() // => { money: 500000, awesomeness: 0 }

The store updates the state according to the action you pass to it. There are no weird side effects, just simple input output logic.

Wiring up React

So how do we wire the store up to a view? I mean, all this stuff is fun, but we want to interact with it right, otherwise what’s the point? Let’s do this example with, yes, React.

First we create a React Component:

import React, { Component }  from 'react'
import { connect, Provider } from 'react-redux'

class MyCoolComponent extends Component {
  constructor(props, context) {
    super(props, context)
    this.giveMoney = this.giveMoney.bind(this)
  }

  giveMoney(e) {
    e.preventDefault()
    this.props.dispatch(addMoney(10))
  }

  render() {
    const { money, awesomeness } = this.props
    return (
      <div>
        <p>`I have ${money}€, and I\'m ${awesomeness} awesome!`</p>
        <button onClick={this.giveMoney}>Give me 10€</button>
      </div>
    )
  }
}

This component needs the money and awesomeness props to be passed to it. It will then render a nice message about me and a button to give me more money :). If someone clicks the button, the giveMoney method is called. It will need the dispatch function from the store (also passed via the props) and then pass the addMoney action to the dispatch function.

Now we have to connect the component to redux, so that it can pass us the money and awesomeness props. To do this we will use the package ‘react-redux‘, which will ‘connect‘ our view to our store. This will let the view update automatically when the state of the store changes.

import { connect, Provider } from 'react-redux'

connect((state) => state)(MyCoolComponent)

ReactDOM.render(
  <Provider store={store}>
    <MyCoolComponent>
  </Provider>,
  document.getElementById('container')
)

First we import the connect function and the Provider component from ‘react-redux‘. Then we connect MyCoolComponent to the store. The connect method takes a function that allows you to control how the state is passed to the component. For now just (state) => state will be sufficient. Then we pass MyCoolComponent to the function connect returns, our component is now ready for connection with redux.

At last we render our component into the DOM, but we wrap it in the Provider component. We pass the Provider component the store, the Provider will now connect the ‘connected‘ components inside of it with the store. And that’s it. Our component now updates when the state of the store changes.

Conclusion

I think this is enough for this article. If you are confused right now, don’t worry, it takes some time to get your head around the concept. It’s a bit different from what you might be used to. There is a lot more you can do with redux (like middleware, debug tooling) and it has a very vibrant community around it. The documentation of redux is very good and the library is maintained actively. The only way to learn more about it, is to play with it! Happy playing!

Reference

Related posts

Flux, what and why?

If you are into front-end development, you’ve probably heard or read the term ‘Flux’. What does it mean and why should you care?

Handling async in Redux with Sagas

Using Redux is a nice way to code structured, testable Javascript applications. But there’s still one thing that can prove to be a challenge, asynchronous operations.

Easy React Modals with Hooks and Portals

Modals, there are a thousand ways of implementing them, but the biggest challenge is to keep them simple and flexible. Let’s do that with React Hooks & Portals!