Evolution toward one-way data flow: A quick introduction to Redux
So you’re starting to wake up and realize that React is seriously winning. Perhaps you’ve also heard about Redux but never bothered to dive in to understand what it is and why you should use it.
Well, now is the time to get up to speed and get in the game. I’ll give you a quick back-story then dive into some code.
ONE-WAY DATA FLOW, FTW
Once upon a time, front-end engineers believed they found the Holy Grail with two-way binding. Change some data and the UI magically changes (or vice versa). Robust frameworks like Angular and Ember became popular, and there was much rejoicing.
Yaaaaaaaaaaaay
However, not long into the story, developers realized browser performance was becoming an issue as applications grew, and keeping track of the data flow was arguably an even bigger problem. Large companies like Facebook complained that implementing seemingly trivial things like their chat feature was becoming insanely hard and buggy.
So Facebook introduced a new way to think about data flow:
What are these things, you ask?
Well, let’s look at a simple example of “Hi, Bob” being displayed on the screen.
To update the name, you can send over an “Action” (such as “change it to Jack”). The “Dispatcher” then does the work to update the state of your application (the “Store”), and “View” changes when the Store is updated.
That was probably confusing, so I’ll explain it another way:
- Action: “change it to Jack”
- Dispatcher: “cool, i’ll make it happen”
- Store: “ok, job done”
- View: “Hi, Jack!”
ENTER REDUX
This was all fine and dandy, but developers ended up creating a zillion variations of this “Flux” architecture, which was maddening to everyone who was just trying to build things that worked.
Finally, Redux was born, and together with Redux-React, a powerful way to build web/mobile applications emerged:
UM, WHAT?
Let’s look at some code:
//this is the "React" component that renders the "UI" element
import React, { Component } from ‘react’;
class NameView extends Component {
render() {
return (
<div>Hi, {this.props.name}</div>
)
}
}
And our “Application State” is a simple object
{name: “Bob”}
So we’ll send over an “Action”:
export const UPDATE_NAME = ‘UPDATE_NAME’;
export function updateName(name) {
return {
type: UPDATE_NAME,
name: name
};
}
And have our “reducer” get the job done when we execute updateName(“Jack”):
import * as ActionTypes from ‘../your/actions/file’;
export default function myReducer(state = {}, action) {
switch (action.type) {
case ActionTypes.UPDATE_NAME:
return {name: action.name}
default:
return: state;
}
}
So the new state returned is:
{name: “Jack”}
Finally, without diving into all the nuances (which you can read about here), what react-redux does for us is to allow us to create a special component — commonly called a “container” — that will hook everything together so the updated state properly updates the view component that we created above (commonly called a “presentational” component).
import { connect } from ‘react-redux’;
import * as ActionTypes from '../your/actions/file';
import NameView from '../your/NameView';
function mapStateToProps(state) {
return {
name: state.your_app.name
}
}
function mapDispatchToProps(dispatch, ownProps) {
return {
handleNameChange: (e) => {
dispatch( ActionTypes.updateName("Jack") )
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(NameView); //<-- all one line
This “container” component literally wraps the NameView with two Redux functions that “map the state to props” and “map the dispatch functions to props” that send actions into your reducer.
If you want to continue down the rabbit hole, the careful reader will wonder what the heck state.your_app.name was all about. For that, I’ll refer you to read about the Redux store, and you can also check out a demo chat application I wrote with a Rails5 API/websocket back-end with a Redux/React front-end.
LONG STORY SHORT
In this one-way data flow paradigm, building out presentational components and wrapping them in containers to do the Redux magic solves many problems. Building large, front-end-heavy web applications becomes much more manageable when the UI work is nicely separated from the state-updating work.
Finally, the HUGE bonus here is that you can literally use the same containers, actions, and reducers in React Native to quickly crank out apps for iOS and Android. The presentational components change a bit to use iOS/Android view components instead of HTML elements (e.g. check out this example), but the concepts are wonderfully similar.
Author’s note: this post is part of an article series I’m writing regarding an Introduction to web and mobile development for startup founders. The previous post was A quick introduction to React. Feel free to subscribe to my newsletter and I’ll let you know when I get new content up. Thanks!