If you are here, I assume you are banging your head against the wall trying to figure out Redux for a React project. If you are looking for a quick start for a React project that has Redux already setup then this is a good boilerplate: http://mikechabot.github.io/react-boilerplate/.
But you are probably still a little confused on how the Redux part of that works…or at least I was. Even the demos on the official Redux site have a lot of complications to illustrate more features, so it takes a while to learn the skeleton structure of the library.
For my example. I’m going to assume a few things:
- a working knowledge of React.
- familiarity with the single source of truth behind the Flux model in React
- a node.js / webpack build environment
- familiarity with ES6 JavaScript syntax
The example is going to be an on/off switch and a “light bulb” that also has an on/off state. Here is the code and the deployed example. So there will be one action with two values. Dead. Simple. [If you need more complicated examples, please references some of the more involved tutorials. My example is only meant to show the relationship between the components and the parts of redux.]
I’m going to divide this tutorial into two parts: 1.) the Redux parts and 2.) the connection into React components.
Redux
Let’s first look at the three parts of Redux:
- Store — keeps the single state of truth in its state and dispatches actions
- Action — the thing that’s dispatched to a reducer
- Reducer — a function that takes the previous state and the dispatched action and returns a new state
Here is the diagram I use to help me think about what’s happening.
The one thing I want to draw your attention to is the Dispatch()
and action
part. The action
itself isn’t really doing anything. It’s just a JavaScript object. But the dispatch()
method within the store is what actuates the entire process, not the action.
So let’s look at the example’s code.
Action
export default function flipSwith(value) {
return {
type: 'FLIP_SWITCH',
value
}
}
There is nothing special going on here. We are not importing any dependencies. All this is is just a function that returns a JavaScript object like { type: 'FLIP_SWITCH', value: 'off'}
. The type
property is use by the reducer to determine what type of action is being dispatched. In our simple example the value with be either 'on'
or 'off'
.
Reducer
const lightSwitch = (state='off', action) => {
if (action.type == 'FLIP_SWITCH') {
state = action.value
}
return state
}
export default lightSwitch
Once again there are no dependancies here, so there is nothing special going on here either! The reducer is just a function that takes a state and action as parameters and then does something with those to create a new state. In our case we test the action’s type
property to make sure it’s 'FLIP_SWITCH'
, and if that’s true we set the state to the action’s value. [Either 'on'
or 'off'
.]
The reducer will return a state to the store.
Store
import { createStore} from 'redux'
import reducer from './reducer'
const initialState = 'off'
export default createStore(
reducer,
initialState
)
Alright now we have some fancy dependencies. We use the createStore()
from the redux package to create our store. It takes the reducer and the initial state as parameters. Here I set the initial state to 'off'
and import the reducer we just made. This is how the store is aware of the reducer.
State
A word about state. In our example we are using a string as the state. But this could be a JS object instead. Most other example will use a JS object as the state.
React
I don’t want to get into React components and all of that stuff. The two components I made for this example are pretty simple and use props and an event listener / handler. The details of the components don’t matter all too much. The interaction of Redux and React come from the connection and the mapping functions.
This the entire app.jsx
code. I’ll go through the important parts and it’s not necessarily in order.
connect()
const ConnectedApp = connect(
mapStateToProps,
mapDispatchToProps
)(App)
For this App
is a React component. What we are doing is connecting the dispatch and state from Redux to our App
component. The mapStateToProps
and mapDispatchToProps
are both functions.
mapStateToProps
const mapStateToProps = (state) => {
return {
power: state
}
}
mapStateToProps
takes the state from the store and passes into the connected component’s props. In this case we take the state which is a string and pass it into App
‘s power prop. It returns a JavaScript object that it merges into the components props.
mapDispatchToProps
const mapDispatchToProps = (dispatch, ownProps) => {
return {
onChange: (value) => {
dispatch(flipSwitch(value))
}
}
}
So we can get the state into the component but how to we actuate change in the store? With dispatch! So what we are doing here passing a function to the components props that runs a dispatch()
method inside of it. Here we are passing the a function that takes the on/off value into a flipSwitch action which is dispatched to the reducer which then updates the store’s state, which because of the mapStateToProps
function, updates the components power
props.
Provider
ReactDOM.render(
, document.getElementById('light'))
This isn’t that interesting, but it’s necessary. To make it all work we place our ConnectedApp
component inside a Provider
component which deals with the store.
App
class App extends React.Component {
render () {
return
}
}
Let’s finally look at the App
component. This is what’s connected to the store and the mapStateToProps
and mapDispatchToProps
functions. You can see the power
props are passed to the LightBulb
and LightSwitch
components. These are just props since the mapStateToProps
function is handling all of that for us.
Now the trickier part is taking the onChange
function from mapDispatchToProps
and placing it in the LightSwitch
so it can run the function. This bit of code: onChange={this.props.onChange}
accomplishes that. Which the LightSwitch
changes it passes the on/off value into the function we defined in the mapDispatchToProps
which dispatches our action (with the value) to the store.
Hopefully this tutorial helped you understand how the basic setup of Redux works. To make a more complicated app will require much more advance concepts you can find elsewhere on the internet such as combined reducers, async thunks, more complicated states, etc.