Why I switched from Flux to Redux
React is a great JavaScript framework and there are no signs of it disappearing anywhere soon. Things can get pretty messy though when it comes to managing your application’s state which is why it’s pretty uncommon to see a React application without centralised state management.
I love the Flux architecture and began implementing it as soon as I added React to my developer toolbox and fast realised the problems that could arise from managing unidirectional flow.
It was when I was implementing the Flux architecture into a React Native application that I first encountered the fundamental issues of relying on a dispatcher to handle application actions and state-altercations and for the life of me couldn’t figure out why the Flux dispatcher was completely unresponsive in the application.
Enter Redux…
I’d been eyeing up Redux for quite some time but hadn’t had the chance to learn and implement it’s core concepts until the aforementioned issue with Flux forced my hand to try something new and boy, I’d never go back.
Redux makes it super-easy to make universal apps as the state of your application is stored in a simple object tree within a single store and the only way to mutate the state is to emit an action that triggers a reducer stating how the state tree should be transformed whereas previously in Flux, all actions and state changes would go via the dispatcher, which Redux nicely does away with.
Redux is inspired by Flux and in my opinion, is definitely better. There’s less variety, less complication and the application is easier to test and reproduce bugs. Redux is incredibly simple to implement into any React application and you shouldn’t be fooled by all the fancy terminology it carries.
Actions are just payloads of information that describe the fact that something has happened in your application. They’re the only source of information for the store and are sent via store.dispatch()
Reducers specify how the application’s state changes in response to an action. They are pure functions that are responsible for taking the previous state, plus the action that was called, mutating the current state and then returning the next state.
Store is the object that brings the actions and reducers together and is responsible for holding the application state which can be accessed and updated via the appropriate methods (getState() and dispatch()). There will only be one centralised store in the entire application making state management super-easy :)
No dispatcher! ^_^
Redux has no direct relation to React and you can write Redux applications with Angular, Ember, or even plain-ol’ vanilla JavaScript. It’s important to make that distinction before I move on to the demonstration below as this will be a demonstration purely for React-Redux. The React bindings are not included in Redux by default and you should view the “Usage with React” page to understand how Redux works with React.
So, tying it all together… I’ll create a nice and simple click counter! I’ll also stick with the React Native theme but porting this to React Web is super simple.
Entry Point — index.js
Uses a special React Redux component called <Provider> which magically makes the application store available to all container components in the application without having to explicitly pass it and is required once when you render the root component.
import React, {
PropTypes,
Component
} from 'react'
import { AppRegistry } from 'react-native'
import { createStore } from 'redux'
import { Provider } from 'react-redux'import CounterButton from './components/CounterButton';
import CounterDisplay from './components/CounterDisplay';let store = createStore(clickCounterApp)class ClickCounter extends React.Component {
render () {
return (
<Provider store={store}>
<CounterButton />
<CounterDisplay />
</Provider>
)
}
}AppRegistry.registerComponent('clickCounter', () => ClickCounter);
Component — CounterButton
This component triggers the incrementCounter action defined in actions.js.
import React, { Component } from 'react'
import { View, TouchableNativeFeedback, Text } from 'react-native';
import { incrementCounter } from '../actions.js'class CounterButton extends React.Component {
render () {
return (
<TouchableNativeFeedback onPress={incrementCounter()}>
<View>
<Text>Touch me!</Text>
</View>
</TouchableNativeFeedback>
)
}
}export default CounterButton;
Component — CounterDisplay
This component listens to the applications state and returns the click counter value inside the state as props into the component. mapStateToProps
isn’t just the locate state of the component/object but the single state of the entire redux application, enabling universal updates to be super-easy :)
import React, { Component } from 'react'
import { View, TouchableNativeFeedback, Text } from 'react-native';
import { connect } from 'react-redux';class CounterDisplay extends React.Component {
render () {
return (
<View>
<Text>{this.props.value}</Text>
</View>
)
}
}function mapStateToProps (state) {
return {
value: state.counter.value
}
}export default connect(mapStateToProps)(CounterDisplay);
Actions — actions.js
Containers the action which will trigger a state update.
export const incrementCounter = () => {
return {
type: 'INCREMENT_COUNTER'
}
}
Reducers — reducers.js
Listens for the INCREMENT_COUNTER
action and updates the application state.
const counter = (state = { value: 0 }, action) => {
switch (action.type) {
case 'INCREMENT_COUNTER':
return { ...state, value: action.value }
default:
return state
}
}export default counter;