Red Shift

The official Infinite Red publication for React Native design & development. We’re a fully distributed team building world-class apps for over 20 years for clients all around the world.

Follow publication

A Tour of React Native — Part 2: Redux & Friends

Steve Kellock
Red Shift
Published in
7 min readMar 9, 2016

--

Welcome to Part 2 in the tour. Part 1 covered the visual parts of React Native.

Next, we’ll check out the non-visual parts of building a React Native app.

This time around, we’re not reviewing what’s available in React Native, but instead, exploring great solutions from the developer ecosystem.

It’s pretty hard to have a conversation about a React Native app without talking about how to handle global state.

React Native has a way of dealing with state that’s very basic and simple via setState(). That might be perfect for a tiny 1-screen app, but chances are you need something a bit more.

Since Redux is the most popular library in this space right now, this article will focus on that.

What Redux Aims To Solve

If you’ve built apps before, you probably recognize these things:

  • Hard to track down timing bugs
  • Difficulty following the flow of the program
  • Whack-a-mole (fixing things breaks other things)
  • Wrong or stale data on different parts of the UI

These are the problems Redux (and a few plugins) can solve.

Redux brings to the table some amazing (and simple) ideas for dealing with state and keeping order as your app grows.

Granted, it is a little more complicated than just “Here, hold this information.” But rest assured, with these few little rules and ideas, your app holds strong and consistent even at much larger sizes.

What Does Global State Look Like?

Consider this Pac-Man game:

The global state tree might look something like this:

{
hero: {
livesLeft: 2,
score: 20,
empowered: false,
direction: 'left'
},
enemies: {
inky: { status: 'waiting' },
blinky: { status: 'hunting' },
pinky: { status: 'emerging' },
clyde: { status: 'waiting' }
},
board: {
level: 1,
pelletsEaten: 2,
powerupsEaten: 0,
cherriesUp: false
}
}

The Redux Landscape

There are 3 main players in the Redux game. Actions, Reducers, and the Store.

Actions

  • JavaScript object
  • contains a string type property
  • instructions describing what should change

Reducers

  • JavaScript function
  • transforms current state into the new state
  • usually 1 function per action type

Store

  • holds the global state tree (a JavaScript object)
  • dispatches actions
  • holds the reducers
  • provides subscriptions to changes of state

Actions

Redux asserts that the way to change state is through a change request (called an Action).

They’re dispatched to other parts of the system to handle “what to do” or “what to change”.

Actions are instructions.

The currency for state changes in a Redux app is the Action object. It can be as simple as a JavaScript object with 1 property called type.

{ type: 'JUMP' }

Often it contains more details though.

{ type: 'LOGIN', user: 'steve', pass: 'secret' }

Actions don’t change state, they describe how state should change. In the previous code example, we don’t actually login.

Action Creators — Optional But Useful

For re-use purposes, you’ll often find something called action creators which are simply functions that create those actions.

// creates a login action (ES6 style)
const createLoginAction = (user, pass) =>
({ type: ‘LOGIN’, user, pass })


// calling example
const loginAction = createLoginAction(‘steve’, ‘pass’)

Reducers

Reducers are functions which change state.

Interface

They accept two parameters: the current state and an action. They return a brand new state object.

const funPhasers = (state = {}, action) =>
({ ...state, phasers: 'fun' })

Switch Statement

Often there’s a 1:1 relationship between action types and reducers. Since all actions are dispatched to all reducers, you’ll usually see a switch statement direct traffic.

See the default condition of the case statement? It’s important that you return back the same state if you don’t plan to change anything.

const myReducer = (state = {}, action) => {
switch (action.type) {
case 'WARP':
return { ...state, speed: action.speed }
case 'SHIELDS_UP':
return { ...state, shields: true }
default:
return state
}
}

Pure Functions Are Side-Effect Free

It is important for reducers to be pure functions. Pure functions do not cause side effects. They do not connect to web servers, they don’t call asynchronous code, they do not create data based on randomness or current time.

When given the same inputs, they produce the same outputs.

Separate By Feature

Reducers are often grouped together. In our Pac-Man example, we had 3 top-level items: hero, enemies, and board. In an app, these would be 3 different files and would be responsible for managing their parts of the tree.

For example, the hero reducer would be responsible for updating state based on the score, lives left, etc.

Then Combine Into Root Reducer

All the reducers in your app are combined into a single reducer called a root reducer. Redux has a tool for creating this.

// Reducers/index.jsimport { combineReducers } from ‘redux’
import hero from ‘./HeroReducer’
import enemies from ‘./EnemiesReducer’
import board from ‘./BoardReducer’
// glue all the reducers together
export default combineReducers({
hero,
enemies,
board
})

This naming and combining is how you can trickle in new features without creating a monolithic beast.

Immutability Libraries— Optional But Useful

Another tenant of Redux is state is never to be modified. When changing state, create new objects instead. The spread (…) operator helps significantly, however, when you’re ready, step up into Immutable.js or seamless-immutable.

Store

The Redux Store does a few things, but primarily, it holds everything together. Once setup, you tend to use it indirectly.

The store holds the global state tree. For read-only access, you can grab the data by calling getState().

It also provides a dispatch() function which is how actions are introduced into the system.

The store holds the reducers and calls them all when actions are dispatched.

Lastly, the store provides a subscription mechanism for things like the UI to be notified when parts of the state tree change.

Here’s how a store might be created:

import { createStore } from 'redux'
import RootReducer from '../Reducers/'
const store = createStore(RootReducer)

Middleware

The Redux Store can also be extended by providing plugins in the form of middleware. There are 3rd party middleware out there to do things from persistence to logging to flow control.

React-Redux

Redux by itself knows nothing about React.

So to glue the two worlds together, react-redux was created. It exists to make it easy to grab data out of the state tree and to dispatch actions from React components.

Provider

A <Provider> tag will wrap your root component and via the magic of Context passing, will provide your one-and-only Redux Store to any component nested within.

Provider is one of those things you set up once and forget it exists.

import { Provider } from 'react-redux'// creates our Redux store (elsewhere)
const store = configureStore()

class Root extends React.Component {
render () {
return (
<Provider store={ store }>
<TheRestOfYourApp />
</Provider>
)
}
}

Provider allows any component you create to become Redux-aware.

Container Components

Any of your React components can opt into Redux. The ones that do are called “containers” or “smart components”. The ones that don’t are called “presentational” components or “dumb” components.

For example, if you need to display the value of a number that lives in the state tree, you’ll need your component to connect to Redux.

By wrapping your component in a connect() call, you have the ability to bestow extra props on your component. These extra props can come from the Redux state tree. And whenever the state tree changes, your component will be given the new props automatically.

import { connect } from 'react-redux'// a standard react component
class Scoreboard extends React.Component {
render () {
return <Text>{ this.props.score }</Text>
}
}
// add some more props that come from the global state tree
const mapStateToProps = (state) => {
return {
score: state.hero.score
}
}

// upgrade our component to become Redux-aware
export default connect(mapStateToProps)(Scoreboard)

When you connect() to Redux to become a smart component, you also get a property which is a function called dispatch. This how you hand off actions to the Redux store.

Selectors — Optional But Useful

Another thing to consider when you get building is creating selectors. Inside your mapStateToProps() you’re going to find a bunch of similar code. I highly recommend extracting these into their own files. It both simplifies your smart components and provides a much smaller set of functionality you can test on its own.

Here’s an example:

// Selectors/EnemySelectors.jsimport R from ‘ramda’export const allEnemiesEaten = (state) =>
R.pipe(
R.values,
R.pluck('status'),
R.filter(R.equals('waiting')),
R.length,
R.equals(4)
)(state.enemies)

In your mapStateToProps(), you’d have something that looks like this:

const mapStateToProps = (state) => {
return {
allEnemiesEaten: EnemySelectors.allEnemiesEaten(state)
}
}

Sagas

Redux is only concerned with data.

So if Redux is all about dodging side-effects, Sagas are all about embracing them. It’s a very complimentary setup.

Sagas are a fantastic spot to put control flow logic or asynchronous code such as talking to an API.

For more information about the redux-saga library, check out my previous article.

Where To Next?

So that’s what’s available in Redux-land!

A bright future for React Native!

It’s important to remember that this technology is just getting started. I think we’ll be seeing some reductions in the amount of boilerplate code we write soon. I imagine a few months from now we’ll start seeing frameworks sit on top of Redux and simplify a lot of these patterns.

Meanwhile, at Infinite Red, we’re putting together a base project which glues some of these ideas to help kickstart our new projects. Stop by and star it up. Open some issues if you have some questions!

Stay tuned for the final chapter of the “A Tour of React Native” where we explore the tooling, language, build system and developer ecosystem!

I’m @skellock on Twitter. Press ❤ below if you’re on team Redux.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Published in Red Shift

The official Infinite Red publication for React Native design & development. We’re a fully distributed team building world-class apps for over 20 years for clients all around the world.

Written by Steve Kellock

In the red corner. Weighing in at 9001 pounds. Specializing in React Native and other voodoo…

Responses (6)

Write a response