Redux Documentation 2016-12-06
Redux Documentation 2016-12-06
of Contents
Read Me 1.1
Introduction 1.2
Motivation 1.2.1
Examples 1.2.6
Basics 1.3
Actions 1.3.1
Reducers 1.3.2
Store 1.3.3
Advanced 1.4
Async Actions 1.4.1
Async Flow 1.4.2
Middleware 1.4.3
1
Writing Tests 1.5.5
General 1.6.1
Reducers 1.6.2
2
Store 1.9.2
combineReducers 1.9.3
applyMiddleware 1.9.4
bindActionCreators 1.9.5
compose 1.9.6
Feedback 1.12
3
Read Me
(If you're looking for a WordPress framework, check out Redux Framework.)
It helps you write applications that behave consistently, run in different environments
(client, server, and native), and are easy to test. On top of that, it provides a great
developer experience, such as live code editing combined with a time traveling
debugger.
You can use Redux together with React, or with any other view library.
Testimonials
Love what you're doing with Redux
I asked for comments on Redux in FB's internal JS discussion group, and it was
universally praised. Really awesome work.
It's cool that you are inventing a better Flux by not doing Flux at all.
4
Read Me
Developer Experience
I wrote Redux while working on my React Europe talk called Hot Reloading with Time
Travel. My goal was to create a state management library with minimal API but
completely predictable behavior, so it is possible to implement logging, hot reloading,
time travel, universal apps, record and replay, without any buy-in from the developer.
Influences
Redux evolves the ideas of Flux, but avoids its complexity by taking cues from Elm.
Whether you have used them or not, Redux only takes a few minutes to get started with.
Installation
To install the stable version:
If you're not, you can access these files on unpkg, download them, or point your
package manager to them.
If you don't use a module bundler, it's also fine. The redux npm package includes
precompiled production and development UMD builds in the dist folder. They can be
used directly without a bundler and are thus compatible with many popular JavaScript
module loaders and environments. For example, you can drop a UMD build as a
<script> tag on the page, or tell Bower to install it. The UMD builds make Redux
5
Read Me
The Redux source code is written in ES2015 but we precompile both CommonJS and
UMD builds to ES5 so they work in any modern browser. You don't need to use Babel or
a module bundler to get started with Redux.
Complementary Packages
Most likely, you'll also need the React bindings and the developer tools.
Note that unlike Redux itself, many packages in the Redux ecosystem don't provide
UMD builds, so we recommend using CommonJS module bundlers like Webpack and
Browserify for the most comfortable development experience.
The Gist
The whole state of your app is stored in an object tree inside a single store.
The only way to change the state tree is to emit an action, an object describing what
happened.
To specify how the actions transform the state tree, you write pure reducers.
That's it!
6
Read Me
/**
* This is a reducer, a pure function with (state, action) => state signature.
* It describes how an action transforms the state into the next state.
*
* The shape of the state is up to you: it can be a primitive, an array, an object
,
* or even an Immutable.js data structure. The only important part is that you sho
uld
* not mutate the state object, but return a new object if the state changes.
*
* In this example, we use a `switch` statement and strings, but you can use a hel
per that
* follows a different convention (such as function maps) if it makes sense for yo
ur
* project.
*/
function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
}
store.subscribe(() =>
console.log(store.getState())
)
7
Read Me
Instead of mutating the state directly, you specify the mutations you want to happen with
plain objects called actions. Then you write a special function called a reducer to decide
how every action transforms the entire application's state.
If you're coming from Flux, there is a single important difference you need to understand.
Redux doesn't have a Dispatcher or support many stores. Instead, there is just a single
store with a single root reducing function. As your app grows, instead of adding stores,
you split the root reducer into smaller reducers independently operating on the different
parts of the state tree. This is exactly like how there is just one root component in a
React app, but it is composed out of many small components.
This architecture might seem like an overkill for a counter app, but the beauty of this
pattern is how well it scales to large and complex apps. It also enables very powerful
developer tools, because it is possible to trace every mutation to the action that caused
it. You can record user sessions and reproduce them just by replaying every action.
8
Read Me
Sandrino Di Mattia
Plowing through @dan_abramov 'Getting Started with Redux' - its amazing how
much simpler concepts get with video.
Chris Dhanaraj
Eddie Zaneski
Come for the name hype. Stay for the rock solid fundamentals. (Thanks, and
great job @dan_abramov and @eggheadio!)
Dan
Laurence Roberts
Documentation
Introduction
Basics
Advanced
Recipes
Troubleshooting
Glossary
9
Read Me
API Reference
For PDF, ePub, and MOBI exports for offline reading, and instructions on how to create
them, please see: paulkogel/redux-offline-docs.
Examples
Counter Vanilla (source)
Counter (source)
Todos (source)
Todos with Undo (source)
TodoMVC (source)
Shopping Cart (source)
Tree View (source)
Async (source)
Universal (source)
Real World (source)
If you're new to the NPM ecosystem and have troubles getting a project up and running,
or aren't sure where to paste the gist above, check out simplest-redux-example that uses
Redux together with React and Browserify.
Discussion
Join the #redux channel of the Reactiflux Discord community.
Thanks
The Elm Architecture for a great intro to modeling state updates with reducers;
Turning the database inside-out for blowing my mind;
Developing ClojureScript with Figwheel for convincing me that re-evaluation should
just work;
Webpack for Hot Module Replacement;
Flummox for teaching me to approach Flux without boilerplate or singletons;
disto for a proof of concept of hot reloadable Stores;
NuclearJS for proving this architecture can be performant;
Om for popularizing the idea of a single state atom;
Cycle for showing how often a function is the best tool;
React for the pragmatic innovation.
10
Read Me
Special thanks to Jamie Paton for handing over the redux NPM package name.
Logo
You can find the official logo on GitHub.
Change Log
This project adheres to Semantic Versioning.
Every release, along with the migration instructions, is documented on the Github
Releases page.
Patrons
The work on Redux was funded by the community.
Webflow
Ximedes
License
MIT
11
Introduction
Introduction
Motivation
Core Concepts
Three Principles
Prior Art
Ecosystem
Examples
12
Motivation
Motivation
As the requirements for JavaScript single-page applications have become increasingly
complicated, our code must manage more state than ever before. This state can
include server responses and cached data, as well as locally created data that has not
yet been persisted to the server. UI state is also increasing in complexity, as we need to
manage active routes, selected tabs, spinners, pagination controls, and so on.
Managing this ever-changing state is hard. If a model can update another model, then a
view can update a model, which updates another model, and this, in turn, might cause
another view to update. At some point, you no longer understand what happens in your
app as you have lost control over the when, why, and how of its state. When a
system is opaque and non-deterministic, it's hard to reproduce bugs or add new
features.
As if this wasn't bad enough, consider the new requirements becoming common in
front-end product development. As developers, we are expected to handle optimistic
updates, server-side rendering, fetching data before performing route transitions, and so
on. We find ourselves trying to manage a complexity that we have never had to deal with
before, and we inevitably ask the question: is it time to give up? The answer is no.
This complexity is difficult to handle as we're mixing two concepts that are very hard
for the human mind to reason about: mutation and asynchronicity. I call them Mentos
and Coke. Both can be great in separation, but together they create a mess. Libraries
like React attempt to solve this problem in the view layer by removing both asynchrony
and direct DOM manipulation. However, managing the state of your data is left up to
you. This is where Redux enters.
Following in the steps of Flux, CQRS, and Event Sourcing, Redux attempts to make
state mutations predictable by imposing certain restrictions on how and when updates
can happen. These restrictions are reflected in the three principles of Redux.
13
Core Concepts
Core Concepts
Redux itself is very simple.
Imagine your apps state is described as a plain object. For example, the state of a todo
app might look like this:
{
todos: [{
text: 'Eat food',
completed: true
}, {
text: 'Exercise',
completed: false
}],
visibilityFilter: 'SHOW_COMPLETED'
}
This object is like a model except that there are no setters. This is so that different
parts of the code cant change the state arbitrarily, causing hard-to-reproduce bugs.
To change something in the state, you need to dispatch an action. An action is a plain
JavaScript object (notice how we dont introduce any magic?) that describes what
happened. Here are a few example actions:
Enforcing that every change is described as an action lets us have a clear understanding
of whats going on in the app. If something changed, we know why it changed. Actions
are like breadcrumbs of what has happened. Finally, to tie state and actions together, we
write a function called a reducer. Again, nothing magic about itits just a function that
takes state and action as arguments, and returns the next state of the app. It would be
hard to write such function for a big app, so we write smaller functions managing parts of
the state:
14
Core Concepts
And we write another reducer that manages the complete state of our app by calling
those two reducers for the corresponding state keys:
This is basically the whole idea of Redux. Note that we havent used any Redux APIs. It
comes with a few utilities to facilitate this pattern, but the main idea is that you describe
how your state is updated over time in response to action objects, and 90% of the code
you write is just plain JavaScript, with no use of Redux itself, its APIs, or any magic.
15
Three Principles
Three Principles
Redux can be described in three fundamental principles:
This makes it easy to create universal apps, as the state from your server can be
serialized and hydrated into the client with no extra coding effort. A single state tree also
makes it easier to debug or introspect an application; it also enables you to persist your
app's state in development, for a faster development cycle. Some functionality which has
been traditionally difficult to implement - Undo/Redo, for example - can suddenly
become trivial to implement, if all of your state is stored in a single tree.
console.log(store.getState())
/* Prints
{
visibilityFilter: 'SHOW_ALL',
todos: [
{
text: 'Consider using Redux',
completed: true,
},
{
text: 'Keep all state in a single tree',
completed: false
}
]
}
*/
State is read-only
The only way to change the state is to emit an action, an object describing what
happened.
This ensures that neither the views nor the network callbacks will ever write directly to
the state. Instead, they express an intent to transform the state. Because all changes are
centralized and happen one by one in a strict order, there are no subtle race conditions
16
Three Principles
to watch out for. As actions are just plain objects, they can be logged, serialized, stored,
and later replayed for debugging or testing purposes.
store.dispatch({
type: 'COMPLETE_TODO',
index: 1
})
store.dispatch({
type: 'SET_VISIBILITY_FILTER',
filter: 'SHOW_COMPLETED'
})
Reducers are just pure functions that take the previous state and an action, and return
the next state. Remember to return new state objects, instead of mutating the previous
state. You can start with a single reducer, and as your app grows, split it off into smaller
reducers that manage specific parts of the state tree. Because reducers are just
functions, you can control the order in which they are called, pass additional data, or
even make reusable reducers for common tasks such as pagination.
17
Three Principles
18
Prior Art
Prior Art
Redux has a mixed heritage. It is similar to some patterns and technologies, but is also
different from them in important ways. We'll explore some of the similarities and the
differences below.
Flux
Can Redux be considered a Flux implementation?
(Don't worry, Flux creators approve of it, if that's all you wanted to know.)
Redux was inspired by several important qualities of Flux. Like Flux, Redux prescribes
that you concentrate your model update logic in a certain layer of your application
(stores in Flux, reducers in Redux). Instead of letting the application code directly
mutate the data, both tell you to describe every mutation as a plain object called an
action.
Unlike Flux, Redux does not have the concept of a Dispatcher. This is because it
relies on pure functions instead of event emitters, and pure functions are easy to
compose and don't need an additional entity managing them. Depending on how you
view Flux, you may see this as either a deviation or an implementation detail. Flux has
often been described as (state, action) => state . In this sense, Redux is true to the
Flux architecture, but makes it simpler thanks to pure functions.
Another important difference from Flux is that Redux assumes you never mutate your
data. You can use plain objects and arrays for your state just fine, but mutating them
inside the reducers is strongly discouraged. You should always return a new object,
which is easy with the object spread operator proposal, or with a library like Immutable.
While it is technically possible to write impure reducers that mutate the data for
performance corner cases, we actively discourage you from doing this. Development
features like time travel, record/replay, or hot reloading will break. Moreover it doesn't
seem like immutability poses performance problems in most real apps, because, as Om
demonstrates, even if you lose out on object allocation, you still win by avoiding
expensive re-renders and re-calculations, as you know exactly what changed thanks to
reducer purity.
19
Prior Art
Elm
Elm is a functional programming language inspired by Haskell and created by Evan
Czaplicki. It enforces a model view update architecture, where the update has the
following signature: (action, state) => state . Elm updaters serve the same purpose
as reducers in Redux.
Unlike Redux, Elm is a language, so it is able to benefit from many things like enforced
purity, static typing, out of the box immutability, and pattern matching (using the case
expression). Even if you don't plan to use Elm, you should read about the Elm
architecture, and play with it. There is an interesting JavaScript library playground
implementing similar ideas. We should look there for inspiration on Redux! One way that
we can get closer to the static typing of Elm is by using a gradual typing solution like
Flow.
Immutable
Immutable is a JavaScript library implementing persistent data structures. It is
performant and has an idiomatic JavaScript API.
Immutable and most similar libraries are orthogonal to Redux. Feel free to use them
together!
Redux doesn't care how you store the stateit can be a plain object, an
Immutable object, or anything else. You'll probably want a (de)serialization
mechanism for writing universal apps and hydrating their state from the server, but other
than that, you can use any data storage library as long as it supports immutability. For
example, it doesn't make sense to use Backbone for Redux state, because Backbone
models are mutable.
Note that, even if your immutable library supports cursors, you shouldn't use them in a
Redux app. The whole state tree should be considered read-only, and you should use
Redux for updating the state, and subscribing to the updates. Therefore writing via
cursor doesn't make sense for Redux. If your only use case for cursors is decoupling
the state tree from the UI tree and gradually refining the cursors, you should look
at selectors instead. Selectors are composable getter functions. See reselect for a
really great and concise implementation of composable selectors.
Baobab
20
Prior Art
Baobab is another popular library implementing immutable API for updating plain
JavaScript objects. While you can use it with Redux, there is little benefit in using them
together.
Most of the functionality Baobab provides is related to updating the data with cursors,
but Redux enforces that the only way to update the data is to dispatch an action.
Therefore they solve the same problem differently, and don't complement each other.
Unlike Immutable, Baobab doesn't yet implement any special efficient data structures
under the hood, so you don't really win anything from using it together with Redux. It's
easier to just use plain objects in this case.
Rx
Reactive Extensions (and their undergoing modern rewrite) are a superb way to manage
the complexity of asynchronous apps. In fact there is an effort to create a library that
models human-computer interaction as interdependent observables.
Does it make sense to use Redux together with Rx? Sure! They work great together. For
example, it is easy to expose a Redux store as an observable:
function toObservable(store) {
return {
subscribe({ onNext }) {
let dispose = store.subscribe(() => onNext(store.getState()))
onNext(store.getState())
return { dispose }
}
}
}
Similarly, you can compose different asynchronous streams to turn them into actions
before feeding them to store.dispatch() .
The question is: do you really need Redux if you already use Rx? Maybe not. It's not
hard to re-implement Redux in Rx. Some say it's a two-liner using Rx .scan() method.
It may very well be!
If you're in doubt, check out the Redux source code (there isn't much going on there), as
well as its ecosystem (for example, the developer tools). If you don't care too much
about it and want to go with the reactive data flow all the way, you might want to explore
something like Cycle instead, or even combine it with Redux. Let us know how it goes!
21
Prior Art
22
Ecosystem
Ecosystem
Redux is a tiny library, but its contracts and APIs are carefully chosen to spawn an
ecosystem of tools and extensions.
On this page we will only feature a few of them that the Redux maintainers have vetted
personally. Don't let this discourage you from trying the rest of them! The ecosystem is
growing too fast, and we have a limited time to look at everything. Consider these the
staff picks, and don't hesitate to submit a PR if you've built something wonderful with
Redux.
Learning Redux
Screencasts
Getting Started with Redux Learn the basics of Redux directly from its creator
(30 free videos)
Learn Redux Build a simple photo app that will simplify the core ideas behind
Redux, React Router and React.js
Example Apps
Official Examples A few official examples covering different Redux techniques
SoundRedux A SoundCloud client built with Redux
grafgiti Create graffiti on your GitHub contributions wall
React-lego How to plug into React, one block at a time.
23
Ecosystem
Talks
Live React: Hot Reloading and Time Travel See how constraints enforced by
Redux make hot reloading with time travel easy
Cleaning the Tar: Using React within the Firefox Developer Tools Learn how to
gradually migrate existing MVC applications to Redux
Redux: Simplifying Application State An intro to Redux architecture
Using Redux
Bindings
react-redux React
ng-redux Angular
ng2-redux Angular 2
backbone-redux Backbone
redux-falcor Falcor
deku-redux Deku
polymer-redux - Polymer
ember-redux - Ember.js
Middleware
24
Ecosystem
Routing
react-router-redux Ruthlessly simple bindings to keep React Router and Redux in
sync
redial Universal data fetching and route lifecycle management for React that
works great with Redux
Components
redux-form Keep React form state in Redux
react-redux-form Create forms easily in React with Redux
Enhancers
redux-batched-subscribe Customize batching and debouncing calls to the store
subscribers
redux-history-transitions History transitions based on arbitrary actions
redux-optimist Optimistically apply actions that can be later committed or
reverted
redux-optimistic-ui A reducer enhancer to enable type-agnostic optimistic
updates
redux-undo Effortless undo/redo and action history for your reducers
25
Ecosystem
Utilities
reselect Efficient derived data selectors inspired by NuclearJS
normalizr Normalize nested API responses for easier consumption by the
reducers
redux-actions Reduces the boilerplate in writing reducers and action creators
redux-act An opinionated library for making reducers and action creators
redux-transducers Transducer utilities for Redux
redux-immutable Used to create an equivalent function of Redux
combineReducers that works with Immutable.js state.
DevTools
Redux DevTools An action logger with time travel UI, hot reloading and error
handling for the reducers, first demoed at React Europe
Redux DevTools Extension A Chrome extension wrapping Redux DevTools and
providing additional functionality
DevTools Monitors
Log Monitor The default monitor for Redux DevTools with a tree view
26
Ecosystem
Dock Monitor A resizable and movable dock for Redux DevTools monitors
Slider Monitor A custom monitor for Redux DevTools to replay recorded Redux
actions
Inspector A custom monitor for Redux DevTools that lets you filter actions,
inspect diffs, and pin deep paths in the state to observe their changes
Diff Monitor A monitor for Redux Devtools that diffs the Redux store mutations
between actions
Filterable Log Monitor Filterable tree view monitor for Redux DevTools
Chart Monitor A chart monitor for Redux DevTools
Filter Actions Redux DevTools composable monitor with the ability to filter
actions
Community Conventions
Flux Standard Action A human-friendly standard for Flux action objects
Canonical Reducer Composition An opinionated standard for nested reducer
composition
Ducks: Redux Reducer Bundles A proposal for bundling reducers, action types
and actions
Translations
Chinese
Traditional Chinese
Redux in Russian Russian
Redux en Espaol - Spanish
More
Awesome Redux is an extensive list of Redux-related repositories.
React-Redux Links is a curated list of high-quality articles, tutorials, and related content
for React, Redux, ES6, and more.
27
Ecosystem
28
Examples
Examples
Redux is distributed with a few examples in its source code.
Counter Vanilla
Run the Counter Vanilla example:
cd redux/examples/counter-vanilla
open index.html
It does not require a build system or a view framework and exists to show the raw Redux
API used with ES5.
Counter
Run the Counter example:
cd redux/examples/counter
npm install
npm start
open http://localhost:3000/
This is the most basic example of using Redux together with React. For simplicity, it re-
renders the React component manually when the store changes. In real projects, you
will likely want to use the highly performant React Redux bindings instead.
Todos
Run the Todos example:
29
Examples
cd redux/examples/todos
npm install
npm start
open http://localhost:3000/
This is the best example to get a deeper understanding of how the state updates work
together with components in Redux. It shows how reducers can delegate handling
actions to other reducers, and how you can use React Redux to generate container
components from your presentational components.
cd redux/examples/todos-with-undo
npm install
npm start
open http://localhost:3000/
This is a variation on the previous example. It is almost identical, but additionally shows
how wrapping your reducer with Redux Undo lets you add a Undo/Redo functionality to
your app with a few lines of code.
TodoMVC
Run the TodoMVC example:
cd redux/examples/todomvc
npm install
npm start
open http://localhost:3000/
30
Examples
This is the classical TodoMVC example. It's here for the sake of comparison, but it
covers the same points as the Todos example.
Shopping Cart
Run the Shopping Cart example:
cd redux/examples/shopping-cart
npm install
npm start
open http://localhost:3000/
This example shows important idiomatic Redux patterns that become important as your
app grows. In particular, it shows how to store entities in a normalized way by their IDs,
how to compose reducers on several levels, and how to define selectors alongside the
reducers so the knowledge about the state shape is encapsulated. It also demonstrates
logging with Redux Logger and conditional dispatching of actions with Redux Thunk
middleware.
Tree View
Run the Tree View example:
cd redux/examples/tree-view
npm install
npm start
open http://localhost:3000/
This example demonstrates rendering a deeply nested tree view and representing its
state in a normalized form so it is easy to update from reducers. Good rendering
performance is achieved by the container components granularly subscribing only to the
tree nodes that they render.
31
Examples
Async
Run the Async example:
cd redux/examples/async
npm install
npm start
open http://localhost:3000/
This example includes reading from an asynchronous API, fetching data in response to
user input, showing loading indicators, caching the response, and invalidating the cache.
It uses Redux Thunk middleware to encapsulate asynchronous side effects.
Universal
Run the Universal example:
cd redux/examples/universal
npm install
npm start
open http://localhost:3000/
This is a basic demonstration of server rendering with Redux and React. It shows how to
prepare the initial store state on the server, and pass it down to the client so the client
store can boot up from an existing state.
Real World
Run the Real World example:
32
Examples
cd redux/examples/real-world
npm install
npm start
open http://localhost:3000/
This is the most advanced example. It is dense by design. It covers keeping fetched
entities in a normalized cache, implementing a custom middleware for API calls,
rendering partially loaded data, pagination, caching responses, displaying error
messages, and routing. Additionally, it includes Redux DevTools.
More Examples
You can find more examples in Awesome Redux.
33
Basics
Basics
Don't be fooled by all the fancy talk about reducers, middleware, store enhancers
Redux is incredibly simple. If you've ever built a Flux application, you will feel right at
home. If you're new to Flux, it's easy too!
In this guide, we'll walk through the process of creating a simple Todo app.
Actions
Reducers
Store
Data Flow
Usage with React
Example: Todo List
34
Actions
Actions
First, let's define some actions.
Actions are payloads of information that send data from your application to your store.
They are the only source of information for the store. You send them to the store using
store.dispatch() .
{
type: ADD_TODO,
text: 'Build my first Redux app'
}
Actions are plain JavaScript objects. Actions must have a type property that indicates
the type of action being performed. Types should typically be defined as string
constants. Once your app is large enough, you may want to move them into a separate
module.
Note on Boilerplate
You don't have to define action type constants in a separate file, or even to define
them at all. For a small project, it might be easier to just use string literals for
action types. However, there are some benefits to explicitly declaring constants in
larger codebases. Read Reducing Boilerplate for more practical tips on keeping
your codebase clean.
Other than type , the structure of an action object is really up to you. If you're
interested, check out Flux Standard Action for recommendations on how actions could
be constructed.
We'll add one more action type to describe a user ticking off a todo as completed. We
refer to a particular todo by index because we store them in an array. In a real app, it is
wiser to generate a unique ID every time something new is created.
35
Actions
{
type: TOGGLE_TODO,
index: 5
}
It's a good idea to pass as little data in each action as possible. For example, it's better
to pass index than the whole todo object.
Finally, we'll add one more action type for changing the currently visible todos.
{
type: SET_VISIBILITY_FILTER,
filter: SHOW_COMPLETED
}
Action Creators
Action creators are exactly thatfunctions that create actions. It's easy to conflate the
terms action and action creator, so do your best to use the proper term.
function addTodo(text) {
return {
type: ADD_TODO,
text
}
}
In traditional Flux, action creators often trigger a dispatch when invoked, like so:
function addTodoWithDispatch(text) {
const action = {
type: ADD_TODO,
text
}
dispatch(action)
}
Instead, to actually initiate a dispatch, pass the result to the dispatch() function:
36
Actions
dispatch(addTodo(text))
dispatch(completeTodo(index))
Alternatively, you can create a bound action creator that automatically dispatches:
boundAddTodo(text)
boundCompleteTodo(index)
The dispatch() function can be accessed directly from the store as store.dispatch() ,
but more likely you'll access it using a helper like react-redux's connect() . You can use
bindActionCreators() to automatically bind many action creators to a dispatch()
function.
Action creators can also be asynchronous and have side-effects. You can read about
async actions in the advanced tutorial to learn how to handle AJAX responses and
compose action creators into async control flow. Don't skip ahead to async actions until
you've completed the basics tutorial, as it covers other important concepts that are
prerequisite for the advanced tutorial and async actions.
Source Code
actions.js
37
Actions
/*
* action types
*/
/*
* other constants
*/
/*
* action creators
*/
Next Steps
Now let's define some reducers to specify how the state updates when you dispatch
these actions!
38
Reducers
Reducers
Actions describe the fact that something happened, but don't specify how the
application's state changes in response. This is the job of reducers.
You'll often find that you need to store some data, as well as some UI state, in the state
tree. This is fine, but try to keep the data separate from the UI state.
{
visibilityFilter: 'SHOW_ALL',
todos: [
{
text: 'Consider using Redux',
completed: true,
},
{
text: 'Keep all state in a single tree',
completed: false
}
]
}
39
Reducers
Note on Relationships
In a more complex app, you're going to want different entities to reference each
other. We suggest that you keep your state as normalized as possible, without
any nesting. Keep every entity in an object stored with an ID as a key, and use
IDs to reference it from other entities, or lists. Think of the app's state as a
database. This approach is described in normalizr's documentation in detail. For
example, keeping todosById: { id -> todo } and todos: array<id> inside the
state would be a better idea in a real app, but we're keeping the example simple.
Handling Actions
Now that we've decided what our state object looks like, we're ready to write a reducer
for it. The reducer is a pure function that takes the previous state and an action, and
returns the next state.
It's called a reducer because it's the type of function you would pass to
Array.prototype.reduce(reducer, ?initialValue) . It's very important that the reducer
We'll explore how to perform side effects in the advanced walkthrough. For now, just
remember that the reducer must be pure. Given the same arguments, it should
calculate the next state and return it. No surprises. No side effects. No API calls.
No mutations. Just a calculation.
With this out of the way, let's start writing our reducer by gradually teaching it to
understand the actions we defined earlier.
We'll start by specifying the initial state. Redux will call our reducer with an undefined
state for the first time. This is our chance to return the initial state of our app:
40
Reducers
const initialState = {
visibilityFilter: VisibilityFilters.SHOW_ALL,
todos: []
}
One neat trick is to use the ES6 default arguments syntax to write this in a more
compact way:
Note that:
mutate the first argument. You must supply an empty object as the first parameter.
You can also enable the object spread operator proposal to write { ...state,
...newState } instead.
41
Reducers
2. We return the previous state in the default case. It's important to return the
previous state for any unknown action.
Note on Object.assign
You'll need to either use a polyfill, a Babel plugin, or a helper from another library
like _.assign() .
The switch statement is not the real boilerplate. The real boilerplate of Flux is
conceptual: the need to emit an update, the need to register the Store with a
Dispatcher, the need for the Store to be an object (and the complications that
arise when you want a universal app). Redux solves these problems by using
pure reducers instead of event emitters.
It's unfortunate that many still choose a framework based on whether it uses
switch statements in the documentation. If you don't like switch , you can use a
42
Reducers
Just like before, we never write directly to state or its fields, and instead we return new
objects. The new todos is equal to the old todos concatenated with a single new item
at the end. The fresh todo was constructed using the data from the action.
case TOGGLE_TODO:
return Object.assign({}, state, {
todos: state.todos.map((todo, index) => {
if (index === action.index) {
return Object.assign({}, todo, {
completed: !todo.completed
})
}
return todo
})
})
Because we want to update a specific item in the array without resorting to mutations,
we have to create a new array with the same items except the item at the index. If you
find yourself often writing such operations, it's a good idea to use a helper like
immutability-helper, updeep, or even a library like Immutable that has native support for
deep updates. Just remember to never assign to anything inside the state unless you
clone it first.
43
Reducers
Splitting Reducers
Here is our code so far. It is rather verbose:
depend on one another and more consideration is required, but in our case we can
easily split updating todos into a separate function:
44
Reducers
Note that todos also accepts state but it's an array! Now todoApp just gives it the
slice of the state to manage, and todos knows how to update just that slice. This is
called reducer composition, and it's the fundamental pattern of building Redux
apps.
Let's explore reducer composition more. Can we also extract a reducer managing just
visibilityFilter ? We can:
45
Reducers
Now we can rewrite the main reducer as a function that calls the reducers managing
parts of the state, and combines them into a single object. It also doesn't need to know
the complete initial state anymore. It's enough that the child reducers return their initial
state when given undefined at first.
46
Reducers
Note that each of these reducers is managing its own part of the global state. The
state parameter is different for every reducer, and corresponds to the part of the
state it manages.
This is already looking good! When the app is larger, we can split the reducers into
separate files and keep them completely independent and managing different data
domains.
47
Reducers
Finally, Redux provides a utility called combineReducers() that does the same boilerplate
logic that the todoApp above currently does. With its help, we can rewrite todoApp like
this:
You could also give them different keys, or call functions differently. These two ways to
write a combined reducer are equivalent:
All combineReducers() does is generate a function that calls your reducers with the
slices of state selected according to their keys, and combining their results into a
single object again. It's not magic. And like other reducers, combineReducers() does not
create a new object if all of the reducers provided to it do not change state.
48
Reducers
Source Code
reducers.js
49
Reducers
Next Steps
Next, we'll explore how to create a Redux store that holds the state and takes care of
calling your reducer when you dispatch an action.
50
Reducers
51
Store
Store
In the previous sections, we defined the actions that represent the facts about what
happened and the reducers that update the state according to those actions.
The Store is the object that brings them together. The store has the following
responsibilities:
It's important to note that you'll only have a single store in a Redux application. When
you want to split your data handling logic, you'll use reducer composition instead of
many stores.
It's easy to create a store if you have a reducer. In the previous section, we used
combineReducers() to combine several reducers into one. We will now import it, and
pass it to createStore() .
You may optionally specify the initial state as the second argument to createStore() .
This is useful for hydrating the state of the client to match the state of a Redux
application running on the server.
Dispatching Actions
Now that we have created a store, let's verify our program works! Even without any UI,
we can already test the update logic.
52
Store
You can see how this causes the state held by the store to change:
53
Store
We specified the behavior of our app before we even started writing the UI. We won't do
this in this tutorial, but at this point you can write tests for your reducers and action
creators. You won't need to mock anything because they are just pure functions. Call
them, and make assertions on what they return.
Source Code
index.js
Next Steps
Before creating a UI for our todo app, we will take a detour to see how the data flows in
a Redux application.
54
Data Flow
Data Flow
Redux architecture revolves around a strict unidirectional data flow.
This means that all data in an application follows the same lifecycle pattern, making the
logic of your app more predictable and easier to understand. It also encourages data
normalization, so that you don't end up with multiple, independent copies of the same
data that are unaware of one another.
If you're still not convinced, read Motivation and The Case for Flux for a compelling
argument in favor of unidirectional data flow. Although Redux is not exactly Flux, it
shares the same key benefits.
Think of an action as a very brief snippet of news. Mary liked article 42. or Read
the Redux docs.' was added to the list of todos.
2. The Redux store calls the reducer function you gave it.
The store will pass two arguments to the reducer: the current state tree and the
action. For example, in the todo app, the root reducer might receive something like
this:
55
Data Flow
Note that a reducer is a pure function. It only computes the next state. It should be
completely predictable: calling it with the same inputs many times should produce
the same outputs. It shouldn't perform any side effects like API calls or router
transitions. These should happen before an action is dispatched.
3. The root reducer may combine the output of multiple reducers into a single
state tree.
How you structure the root reducer is completely up to you. Redux ships with a
combineReducers() helper function, useful for splitting the root reducer into
separate functions that each manage one branch of the state tree.
Here's how combineReducers() works. Let's say you have two reducers, one for a
list of todos, and another for the currently selected filter setting:
56
Data Flow
When you emit an action, todoApp returned by combineReducers will call both
reducers:
It will then combine both sets of results into a single state tree:
return {
todos: nextTodos,
visibleTodoFilter: nextVisibleTodoFilter
}
While combineReducers() is a handy helper utility, you don't have to use it; feel free
to write your own root reducer!
4. The Redux store saves the complete state tree returned by the root reducer.
This new tree is now the next state of your app! Every listener registered with
store.subscribe(listener) will now be invoked; listeners may call
Now, the UI can be updated to reflect the new state. If you use bindings like React
Redux, this is the point at which component.setState(newState) is called.
Next Steps
Now that you know how Redux works, let's connect it to a React app.
57
Data Flow
If you're already familiar with the basic concepts and have previously completed
this tutorial, don't forget to check out async flow in the advanced tutorial to learn
how middleware transforms async actions before they reach the reducer.
58
Usage with React
That said, Redux works especially well with libraries like React and Deku because they
let you describe UI as a function of state, and Redux emits state updates in response to
actions.
If you don't use npm, you may grab the latest UMD build from unpkg (either a
development or a production build). The UMD build exports a global called
window.ReactRedux if you add it to your page via a <script> tag.
59
Usage with React
Presentational
Container Components
Components
Aware of
No Yes
Redux
To read
Read data from props Subscribe to Redux state
data
Most of the components we'll write will be presentational, but we'll need to generate a
few container components to connect them to the Redux store.
performance optimizations that are hard to do by hand. For this reason, rather than write
container components, we will generate them using the connect() function provided by
React Redux, as you will see below.
Our design brief is simple. We want to show a list of todo items. On click, a todo item is
crossed out as completed. We want to show a field where the user may add a new todo.
In the footer, we want to show a toggle to show all, only completed, or only active todos.
Presentational Components
I see the following presentational components and their props emerge from this brief:
todos: Array is an array of todo items with { id, text, completed } shape.
60
Usage with React
They describe the look but don't know where the data comes from, or how to change it.
They only render what's given to them. If you migrate from Redux to something else,
you'll be able to keep all these components exactly the same. They have no dependency
on Redux.
Container Components
We will also need some container components to connect the presentational
components to Redux. For example, the presentational TodoList component needs a
container like VisibleTodoList that subscribes to the Redux store and knows how to
apply the current visibility filter. To change the visibility filter, we will provide a
FilterLink container component that renders a Link that dispatches an appropriate
action on click:
VisibleTodoList filters the todos according to the current visibility filter and renders
a TodoList .
FilterLink gets the current visibility filter and renders a Link .
Other Components
Sometimes it's hard to tell if some component should be a presentational component or
a container. For example, sometimes form and function are really coupled together, such
as in case of this tiny component:
Technically we could split it into two components but it might be too early at this stage.
It's fine to mix presentation and logic in a component that is very small. As it grows, it will
be more obvious how to split it, so we'll leave it mixed.
61
Usage with React
Implementing Components
Let's write the components! We begin with the presentational components so we don't
need to think about binding to Redux yet.
Presentational Components
These are all normal React components, so we won't examine them in detail. We write
functional stateless components unless we need to use local state or the lifecycle
methods. This doesn't mean that presentational components have to be functionsit's
just easier to define them this way. If and when you need to add local state, lifecycle
methods, or performance optimizations, you can convert them to classes.
components/Todo.js
Todo.propTypes = {
onClick: PropTypes.func.isRequired,
completed: PropTypes.bool.isRequired,
text: PropTypes.string.isRequired
}
components/TodoList.js
62
Usage with React
TodoList.propTypes = {
todos: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.number.isRequired,
completed: PropTypes.bool.isRequired,
text: PropTypes.string.isRequired
}).isRequired).isRequired,
onTodoClick: PropTypes.func.isRequired
}
components/Link.js
63
Usage with React
return (
<a href="#"
onClick={e => {
e.preventDefault()
onClick()
}}
>
{children}
</a>
)
}
Link.propTypes = {
active: PropTypes.bool.isRequired,
children: PropTypes.node.isRequired,
onClick: PropTypes.func.isRequired
}
components/Footer.js
64
Usage with React
components/App.js
Container Components
Now it's time to hook up those presentational components to Redux by creating some
containers. Technically, a container component is just a React component that uses
store.subscribe() to read a part of the Redux state tree and supply props to a
re-renders. (One result of this is that you shouldn't have to worry about the React
performance suggestion of implementing shouldComponentUpdate yourself.)
To use connect() , you need to define a special function called mapStateToProps that
tells how to transform the current Redux store state into the props you want to pass to a
presentational component you are wrapping. For example, VisibleTodoList needs to
calculate todos to pass to the TodoList , so we define a function that filters the
state.todos according to the state.visibilityFilter , and use it in its
mapStateToProps :
65
Usage with React
In addition to reading the state, container components can dispatch actions. In a similar
fashion, you can define a function called mapDispatchToProps() that receives the
dispatch() method and returns callback props that you want to inject into the
Finally, we create the VisibleTodoList by calling connect() and passing these two
functions:
66
Usage with React
These are the basics of the React Redux API, but there are a few shortcuts and power
options so we encourage you to check out its documentation in detail. In case you are
worried about mapStateToProps creating new objects too often, you might want to learn
about computing derived data with reselect.
containers/FilterLink.js
containers/VisibleTodoList.js
67
Usage with React
Other Components
containers/AddTodo.js
68
Usage with React
return (
<div>
<form onSubmit={e => {
e.preventDefault()
if (!input.value.trim()) {
return
}
dispatch(addTodo(input.value))
input.value = ''
}}>
<input ref={node => {
input = node
}} />
<button type="submit">
Add Todo
</button>
</form>
</div>
)
}
AddTodo = connect()(AddTodo)
application without passing it explicitly. You only need to use it once when you render
the root component:
index.js
69
Usage with React
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
Next Steps
Read the complete source code for this tutorial to better internalize the knowledge you
have gained. Then, head straight to the advanced tutorial to learn how to handle network
requests and routing!
70
Example: Todo List
Entry Point
index.js
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
Action Creators
actions/index.js
71
Example: Todo List
let nextTodoId = 0
export const addTodo = (text) => {
return {
type: 'ADD_TODO',
id: nextTodoId++,
text
}
}
Reducers
reducers/todos.js
72
Example: Todo List
default:
return state
}
}
reducers/visibilityFilter.js
73
Example: Todo List
reducers/index.js
Presentational Components
components/Todo.js
Todo.propTypes = {
onClick: PropTypes.func.isRequired,
completed: PropTypes.bool.isRequired,
text: PropTypes.string.isRequired
}
components/TodoList.js
74
Example: Todo List
TodoList.propTypes = {
todos: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.number.isRequired,
completed: PropTypes.bool.isRequired,
text: PropTypes.string.isRequired
}).isRequired).isRequired,
onTodoClick: PropTypes.func.isRequired
}
components/Link.js
75
Example: Todo List
return (
<a href="#"
onClick={e => {
e.preventDefault()
onClick()
}}
>
{children}
</a>
)
}
Link.propTypes = {
active: PropTypes.bool.isRequired,
children: PropTypes.node.isRequired,
onClick: PropTypes.func.isRequired
}
components/Footer.js
76
Example: Todo List
components/App.js
Container Components
containers/VisibleTodoList.js
77
Example: Todo List
containers/FilterLink.js
78
Example: Todo List
Other Components
containers/AddTodo.js
79
Example: Todo List
return (
<div>
<form onSubmit={e => {
e.preventDefault()
if (!input.value.trim()) {
return
}
dispatch(addTodo(input.value))
input.value = ''
}}>
<input ref={node => {
input = node
}} />
<button type="submit">
Add Todo
</button>
</form>
</div>
)
}
AddTodo = connect()(AddTodo)
80
Advanced
Advanced
In the basics walkthrough, we explored how to structure a simple Redux application. In
this walkthrough, we will explore how AJAX and routing fit into the picture.
Async Actions
Async Flow
Middleware
Usage with React Router
Example: Reddit API
Next Steps
81
Async Actions
Async Actions
In the basics guide, we built a simple todo application. It was fully synchronous. Every
time an action was dispatched, the state was updated immediately.
In this guide, we will build a different, asynchronous application. It will use the Reddit API
to show the current headlines for a selected subreddit. How does asynchronicity fit into
Redux flow?
Actions
When you call an asynchronous API, there are two crucial moments in time: the moment
you start the call, and the moment when you receive an answer (or a timeout).
Each of these two moments usually require a change in the application state; to do that,
you need to dispatch normal actions that will be processed by reducers synchronously.
Usually, for any API request you'll want to dispatch at least three different kinds of
actions:
The reducers may handle this action by toggling an isFetching flag in the state.
This way the UI knows it's time to show a spinner.
The reducers may handle this action by merging the new data into the state they
manage and resetting isFetching . The UI would hide the spinner, and display the
fetched data.
The reducers may handle this action by resetting isFetching . Additionally, some
reducers may want to store the error message so the UI can display it.
{ type: 'FETCH_POSTS' }
{ type: 'FETCH_POSTS', status: 'error', error: 'Oops' }
{ type: 'FETCH_POSTS', status: 'success', response: { ... } }
82
Async Actions
{ type: 'FETCH_POSTS_REQUEST' }
{ type: 'FETCH_POSTS_FAILURE', error: 'Oops' }
{ type: 'FETCH_POSTS_SUCCESS', response: { ... } }
Choosing whether to use a single action type with flags, or multiple action types, is up to
you. It's a convention you need to decide with your team. Multiple types leave less room
for a mistake, but this is not an issue if you generate action creators and reducers with a
helper library like redux-actions.
actions.js
83
Async Actions
These were the actions governed by the user interaction. We will also have another kind
of action, governed by the network requests. We will see how to dispatch them later, but
for now, we just want to define them.
When it's time to fetch the posts for some subreddit, we will dispatch a REQUEST_POSTS
action:
Finally, when the network request comes through, we will dispatch RECEIVE_POSTS :
This is all we need to know for now. The particular mechanism to dispatch these actions
together with network requests will be discussed later.
In a real app, you'd also want to dispatch an action on request failure. We won't
implement error handling in this tutorial, but the real world example shows one of
the possible approaches.
84
Async Actions
This part is often confusing to beginners, because it is not immediately clear what
information describes the state of an asynchronous application, and how to organize it in
a single tree.
We'll start with the most common use case: lists. Web applications often show lists of
things. For example, a list of posts, or a list of friends. You'll need to figure out what sorts
of lists your app can show. You want to store them separately in the state, because this
way you can cache them and only fetch again if necessary.
Here's what the state shape for our Reddit headlines app might look like:
{
selectedSubreddit: 'frontend',
postsBySubreddit: {
frontend: {
isFetching: true,
didInvalidate: false,
items: []
},
reactjs: {
isFetching: false,
didInvalidate: false,
lastUpdated: 1439478405547,
items: [
{
id: 42,
title: 'Confusion about Flux and Relay'
},
{
id: 500,
title: 'Creating a Simple Application Using React JS and Flux Architectu
re'
}
]
}
}
}
85
Async Actions
For every list of items, you'll want to store isFetching to show a spinner,
didInvalidate so you can later toggle it when the data is stale, lastUpdated so
you know when it was fetched the last time, and the items themselves. In a real
app, you'll also want to store pagination state like fetchedPageCount and
nextPageUrl .
In this example, we store the received items together with the pagination
information. However, this approach won't work well if you have nested entities
referencing each other, or if you let the user edit items. Imagine the user wants to
edit a fetched post, but this post is duplicated in several places in the state tree.
This would be really painful to implement.
If you have nested entities, or if you let users edit received entities, you should
keep them separately in the state as if it was a database. In pagination
information, you would only refer to them by their IDs. This lets you always keep
them up to date. The real world example shows this approach, together with
normalizr to normalize the nested API responses. With this approach, your state
might look like this:
86
Async Actions
{
selectedSubreddit: 'frontend',
entities: {
users: {
2: {
id: 2,
name: 'Andrew'
}
},
posts: {
42: {
id: 42,
title: 'Confusion about Flux and Relay',
author: 2
},
100: {
id: 100,
title: 'Creating a Simple Application Using React JS and Flux Archit
ecture',
author: 2
}
}
},
postsBySubreddit: {
frontend: {
isFetching: true,
didInvalidate: false,
items: []
},
reactjs: {
isFetching: false,
didInvalidate: false,
lastUpdated: 1439478405547,
items: [ 42, 100 ]
}
}
}
In this guide, we won't normalize entities, but it's something you should consider
for a more dynamic application.
Handling Actions
Before going into the details of dispatching actions together with network requests, we
will write the reducers for the actions we defined above.
87
Async Actions
reducers.js
function posts(state = {
isFetching: false,
didInvalidate: false,
items: []
}, action) {
switch (action.type) {
case INVALIDATE_SUBREDDIT:
return Object.assign({}, state, {
didInvalidate: true
})
case REQUEST_POSTS:
return Object.assign({}, state, {
isFetching: true,
didInvalidate: false
})
case RECEIVE_POSTS:
return Object.assign({}, state, {
isFetching: false,
didInvalidate: false,
items: action.posts,
lastUpdated: action.receivedAt
})
default:
return state
}
}
88
Async Actions
case INVALIDATE_SUBREDDIT:
case RECEIVE_POSTS:
case REQUEST_POSTS:
return Object.assign({}, state, {
[action.subreddit]: posts(state[action.subreddit], action)
})
default:
return state
}
}
is equivalent to this:
let nextState = {}
nextState[action.subreddit] = posts(state[action.subreddit], action)
return Object.assign({}, state, nextState)
Remember that reducers are just functions, so you can use functional composition and
higher-order functions as much as you feel comfortable.
89
Async Actions
Finally, how do we use the synchronous action creators we defined earlier together with
network requests? The standard way to do it with Redux is to use the Redux Thunk
middleware. It comes in a separate package called redux-thunk . We'll explain how
middleware works in general later; for now, there is just one important thing you need to
know: by using this specific middleware, an action creator can return a function instead
of an action object. This way, the action creator becomes a thunk.
When an action creator returns a function, that function will get executed by the Redux
Thunk middleware. This function doesn't need to be pure; it is thus allowed to have side
effects, including executing asynchronous API calls. The function can also dispatch
actionslike those synchronous actions we defined earlier.
We can still define these special thunk action creators inside our actions.js file:
actions.js
90
Async Actions
dispatch(requestPosts(subreddit))
return fetch(`https://www.reddit.com/r/${subreddit}.json`)
.then(response => response.json())
.then(json =>
dispatch(receivePosts(subreddit, json))
)
Note on fetch
We use fetch API in the examples. It is a new API for making network requests
that replaces XMLHttpRequest for most common needs. Because most browsers
don't yet support it natively, we suggest that you use isomorphic-fetch library:
Be aware that any fetch polyfill assumes a Promise polyfill is already present.
The easiest way to ensure you have a Promise polyfill is to enable Babel's ES6
polyfill in your entry point before any other code runs:
91
Async Actions
How do we include the Redux Thunk middleware in the dispatch mechanism? We use
the applyMiddleware() store enhancer from Redux, as shown below:
index.js
store.dispatch(selectSubreddit('reactjs'))
store.dispatch(fetchPosts('reactjs')).then(() =>
console.log(store.getState())
)
The nice thing about thunks is that they can dispatch results of each other:
actions.js
92
Async Actions
function fetchPosts(subreddit) {
return dispatch => {
dispatch(requestPosts(subreddit))
return fetch(`https://www.reddit.com/r/${subreddit}.json`)
.then(response => response.json())
.then(json => dispatch(receivePosts(subreddit, json)))
}
}
This lets us write more sophisticated async control flow gradually, while the consuming
code can stay pretty much the same:
index.js
store.dispatch(fetchPostsIfNeeded('reactjs')).then(() =>
console.log(store.getState())
)
93
Async Actions
Async action creators are especially convenient for server rendering. You can
create a store, dispatch a single async action creator that dispatches other async
action creators to fetch data for a whole section of your app, and only render after
the Promise it returns, completes. Then your store will already be hydrated with
the state you need before rendering.
Thunk middleware isn't the only way to orchestrate asynchronous actions in Redux:
It is up to you to try a few options, choose a convention you like, and follow it, whether
with, or without the middleware.
Connecting to UI
Dispatching async actions is no different from dispatching synchronous actions, so we
won't discuss this in detail. See Usage with React for an introduction into using Redux
from React components. See Example: Reddit API for the complete source code
discussed in this example.
Next Steps
Read Async Flow to recap how async actions fit into the Redux flow.
94
Async Flow
Async Flow
Without middleware, Redux store only supports synchronous data flow. This is what you
get by default with createStore() .
You may enhance createStore() with applyMiddleware() . It is not required, but it lets
you express asynchronous actions in a convenient way.
example, functions or Promises. Any middleware you use can then interpret anything
you dispatch, and in turn, can pass actions to the next middleware in the chain. For
example, a Promise middleware can intercept Promises and dispatch a pair of begin/end
actions asynchronously in response to each Promise.
When the last middleware in the chain dispatches an action, it has to be a plain object.
This is when the synchronous Redux data flow takes place.
Check out the full source code for the async example.
Next Steps
Now you saw an example of what middleware can do in Redux, it's time to learn how it
actually works, and how you can create your own. Go on to the next detailed section
about Middleware.
95
Middleware
Middleware
You've seen middleware in action in the Async Actions example. If you've used server-
side libraries like Express and Koa, you were also probably already familiar with the
concept of middleware. In these frameworks, middleware is some code you can put
between the framework receiving a request, and the framework generating a response.
For example, Express or Koa middleware may add CORS headers, logging,
compression, and more. The best feature of middleware is that it's composable in a
chain. You can use multiple independent third-party middleware in a single project.
Redux middleware solves different problems than Express or Koa middleware, but in a
conceptually similar way. It provides a third-party extension point between
dispatching an action, and the moment it reaches the reducer. People use Redux
middleware for logging, crash reporting, talking to an asynchronous API, routing, and
more.
This article is divided into an in-depth intro to help you grok the concept, and a few
practical examples to show the power of middleware at the very end. You may find it
helpful to switch back and forth between them, as you flip between feeling bored and
inspired.
Understanding Middleware
While middleware can be used for a variety of things, including asynchronous API calls,
it's really important that you understand where it comes from. We'll guide you through
the thought process leading to middleware, by using logging and crash reporting as
examples.
Problem: Logging
One of the benefits of Redux is that it makes state changes predictable and transparent.
Every time an action is dispatched, the new state is computed and saved. The state
cannot change by itself, it can only change as a consequence of a specific action.
Wouldn't it be nice if we logged every action that happens in the app, together with the
state computed after it? When something goes wrong, we can look back at our log, and
figure out which action corrupted the state.
96
Middleware
Note
If you're using react-redux or similar bindings, you likely won't have direct access
to the store instance in your components. For the next few paragraphs, just
assume you pass the store down explicitly.
store.dispatch(addTodo('Use Redux'))
To log the action and state, you can change it to something like this:
console.log('dispatching', action)
store.dispatch(action)
console.log('next state', store.getState())
This produces the desired effect, but you wouldn't want to do it every time.
97
Middleware
We could end this here, but it's not very convenient to import a special function every
time.
98
Middleware
Wouldn't it be useful if, any time an error is thrown as a result of dispatching an action,
we would send it to a crash reporting service like Sentry with the stack trace, the action
that caused the error, and the current state? This way it's much easier to reproduce the
error in development.
However, it is important that we keep logging and crash reporting separate. Ideally we
want them to be different modules, potentially in different packages. Otherwise we can't
have an ecosystem of such utilities. (Hint: we're slowly getting to what middleware is!)
If logging and crash reporting are separate utilities, they might look like this:
function patchStoreToAddLogging(store) {
let next = store.dispatch
store.dispatch = function dispatchAndLog(action) {
console.log('dispatching', action)
let result = next(action)
console.log('next state', store.getState())
return result
}
}
function patchStoreToAddCrashReporting(store) {
let next = store.dispatch
store.dispatch = function dispatchAndReportErrors(action) {
try {
return next(action)
} catch (err) {
console.error('Caught an exception!', err)
Raven.captureException(err, {
extra: {
action,
state: store.getState()
}
})
throw err
}
}
}
If these functions are published as separate modules, we can later use them to patch
our store:
patchStoreToAddLogging(store)
patchStoreToAddCrashReporting(store)
99
Middleware
function logger(store) {
let next = store.dispatch
// Previously:
// store.dispatch = function dispatchAndLog(action) {
We could provide a helper inside Redux that would apply the actual monkeypatching as
an implementation detail:
The fact that we hide it inside the library doesn't alter this fact.
100
Middleware
Why do we even overwrite dispatch ? Of course, to be able to call it later, but there's
also another reason: so that every middleware can access (and call) the previously
wrapped store.dispatch :
function logger(store) {
// Must point to the function returned by the previous middleware:
let next = store.dispatch
dispatch function.
But there's also a different way to enable chaining. The middleware could accept the
next() dispatch function as a parameter instead of reading it from the store instance.
function logger(store) {
return function wrapDispatchToAddLogging(next) {
return function dispatchAndLog(action) {
console.log('dispatching', action)
let result = next(action)
console.log('next state', store.getState())
return result
}
}
}
It's a we need to go deeper kind of moment, so it might take a while for this to make
sense. The function cascade feels intimidating. ES6 arrow functions make this currying
easier on eyes:
101
Middleware
Now middleware takes the next() dispatch function, and returns a dispatch function,
which in turn serves as next() to the middleware to the left, and so on. It's still useful to
have access to some store methods like getState() , so store stays available as the
top-level argument.
102
Middleware
The implementation of applyMiddleware() that ships with Redux is similar, but different
in three important aspects:
It only exposes a subset of the store API to the middleware: dispatch(action) and
getState() .
It does a bit of trickery to make sure that if you call store.dispatch(action) from
your middleware instead of next(action) , the action will actually travel the whole
middleware chain again, including the current middleware. This is useful for
asynchronous middleware, as we have seen previously.
To ensure that you may only apply middleware once, it operates on createStore()
rather than on store itself. Instead of (store, middlewares) => store , its signature
is (...middlewares) => (createStore) => createStore .
103
Middleware
That's it! Now any actions dispatched to the store instance will flow through logger and
crashReporter :
Seven Examples
If your head boiled from reading the above section, imagine what it was like to write it.
This section is meant to be a relaxation for you and me, and will help get your gears
turning.
104
Middleware
Each function below is a valid Redux middleware. They are not equally useful, but at
least they are equally fun.
/**
* Logs all actions and states after they are dispatched.
*/
const logger = store => next => action => {
console.group(action.type)
console.info('dispatching', action)
let result = next(action)
console.log('next state', store.getState())
console.groupEnd(action.type)
return result
}
/**
* Sends crash reports as state is updated and listeners are notified.
*/
const crashReporter = store => next => action => {
try {
return next(action)
} catch (err) {
console.error('Caught an exception!', err)
Raven.captureException(err, {
extra: {
action,
state: store.getState()
}
})
throw err
}
}
/**
* Schedules actions with { meta: { delay: N } } to be delayed by N milliseconds.
* Makes `dispatch` return a function to cancel the timeout in this case.
*/
const timeoutScheduler = store => next => action => {
if (!action.meta || !action.meta.delay) {
return next(action)
}
/**
* Schedules actions with { meta: { raf: true } } to be dispatched inside a rAF lo
105
Middleware
op
* frame. Makes `dispatch` return a function to remove the action from the queue
in
* this case.
*/
const rafScheduler = store => next => {
let queuedActions = []
let frame = null
function loop() {
frame = null
try {
if (queuedActions.length) {
next(queuedActions.shift())
}
} finally {
maybeRaf()
}
}
function maybeRaf() {
if (queuedActions.length && !frame) {
frame = requestAnimationFrame(loop)
}
}
queuedActions.push(action)
maybeRaf()
/**
* Lets you dispatch promises in addition to actions.
* If the promise is resolved, its result will be dispatched as an action.
* The promise is returned from `dispatch` so the caller may handle rejection.
*/
const vanillaPromise = store => next => action => {
if (typeof action.then !== 'function') {
return next(action)
}
return Promise.resolve(action).then(store.dispatch)
}
/**
* Lets you dispatch special actions with a { promise } field.
*
106
Middleware
* This middleware will turn them into a single action at the beginning,
* and a single success (or failure) action when the `promise` resolves.
*
* For convenience, `dispatch` will return the promise so the caller can wait.
*/
const readyStatePromise = store => next => action => {
if (!action.promise) {
return next(action)
}
next(makeAction(false))
return action.promise.then(
result => next(makeAction(true, { result })),
error => next(makeAction(true, { error }))
)
}
/**
* Lets you dispatch a function instead of an action.
* This function will receive `dispatch` and `getState` as arguments.
*
* Useful for early exits (conditions over `getState()`), as well
* as for async control flow (it can `dispatch()` something else).
*
* `dispatch` will return the return value of the dispatched function.
*/
const thunk = store => next => action =>
typeof action === 'function' ?
action(store.dispatch, store.getState) :
next(action)
// You can use all of them! (It doesn't mean you should.)
let todoApp = combineReducers(reducers)
let store = createStore(
todoApp,
applyMiddleware(
rafScheduler,
timeoutScheduler,
thunk,
vanillaPromise,
readyStatePromise,
logger,
crashReporter
)
)
107
Middleware
108
Usage with React Router
router@^2.7.0 .
Configuring Express
If you are serving your index.html from Express:
Configuring WebpackDevServer
109
Usage with React Router
If you are serving your index.html from WebpackDevServer: You can add to your
webpack.config.dev.js:
devServer: {
historyApiFallback: true,
}
First we will need to import <Router /> and <Route /> from React Router. Here's how
to do it:
In a React app, usually you would wrap <Route /> in <Router /> so that when the URL
changes, <Router /> will match a branch of its routes, and render their configured
components. <Route /> is used to declaratively map routes to your application's
component hierarchy. You would declare in path the path used in the URL and in
component the single component to be rendered when the route matches the URL.
However, in our Redux App we will still need <Provider /> . <Provider /> is the higher-
order component provided by React Redux that lets you bind Redux to React (see
Usage with React).
We will wrap <Router /> in <Provider /> so that route handlers can get access to the
store .
110
Usage with React Router
Now the <App /> component will be rendered if the URL matches '/'. Additionally, we
will add the optional (:filter) parameter to / , because we will need it further on
when we try to read the parameter (:filter) from the URL.
You will probably want to remove the hash from the URL (e.g: http://localhost:3000/#/?
_k=4sbb0i ). For doing this, you will need to also import browserHistory from React
Router:
and pass it to the <Router /> in order to remove the hash from the URL:
<Router history={browserHistory}>
<Route path="/(:filter)" component={App} />
</Router>
Unless you are targeting old browsers like IE9, you can always use browserHistory .
components/Root.js
111
Usage with React Router
Root.propTypes = {
store: PropTypes.object.isRequired,
};
containers/FilterLink.js
containers/Footer.js
112
Usage with React Router
Now if you click on <FilterLink /> you will see that your URL will change from
'/complete' , '/active' , '/' . Even if you are going back with your browser, it will use
containers/VisibleTodoList.js
113
Usage with React Router
Right now we are not passing anything to <App /> so ownProps is an empty object. To
filter our todos according to the URL, we want to pass the URL params to
<VisibleTodoList /> .
params property is an object with every param specified in the url. e.g: params will be
Note that we are using ES6 destructuring on the properties to pass in params to
<VisibleTodoList /> .
components/App.js
Next Steps
Now that you know how to do basic routing, you can learn more about React Router API
Redux Router is an experimental library, it lets you keep entirely the state of your
URL inside your redux store. It has the same API with React Router API but has a
smaller community support than react-router.
React Router Redux creates binding between your redux app and react-router
and it keeps them in sync. Without this binding, you will not be able to rewind the
actions with Time Travel. Unless you need this, React-router and Redux can
operates completely apart.
114
Usage with React Router
115
Example: Reddit API
Entry Point
index.js
import 'babel-polyfill'
render(
<Root />,
document.getElementById('root')
)
116
Example: Reddit API
function requestPosts(subreddit) {
return {
type: REQUEST_POSTS,
subreddit
}
}
function fetchPosts(subreddit) {
return dispatch => {
dispatch(requestPosts(subreddit))
return fetch(`https://www.reddit.com/r/${subreddit}.json`)
.then(response => response.json())
.then(json => dispatch(receivePosts(subreddit, json)))
}
}
Reducers
reducers.js
117
Example: Reddit API
import {
SELECT_SUBREDDIT, INVALIDATE_SUBREDDIT,
REQUEST_POSTS, RECEIVE_POSTS
} from './actions'
function posts(state = {
isFetching: false,
didInvalidate: false,
items: []
}, action) {
switch (action.type) {
case INVALIDATE_SUBREDDIT:
return Object.assign({}, state, {
didInvalidate: true
})
case REQUEST_POSTS:
return Object.assign({}, state, {
isFetching: true,
didInvalidate: false
})
case RECEIVE_POSTS:
return Object.assign({}, state, {
isFetching: false,
didInvalidate: false,
items: action.posts,
lastUpdated: action.receivedAt
})
default:
return state
}
}
118
Example: Reddit API
selectedSubreddit
})
Store
configureStore.js
Container Components
containers/Root.js
119
Example: Reddit API
containers/AsyncApp.js
componentDidMount() {
const { dispatch, selectedSubreddit } = this.props
dispatch(fetchPostsIfNeeded(selectedSubreddit))
}
componentWillReceiveProps(nextProps) {
if (nextProps.selectedSubreddit !== this.props.selectedSubreddit) {
const { dispatch, selectedSubreddit } = nextProps
dispatch(fetchPostsIfNeeded(selectedSubreddit))
}
}
handleChange(nextSubreddit) {
this.props.dispatch(selectSubreddit(nextSubreddit))
this.props.dispatch(fetchPostsIfNeeded(nextSubreddit))
}
handleRefreshClick(e) {
e.preventDefault()
120
Example: Reddit API
render() {
const { selectedSubreddit, posts, isFetching, lastUpdated } = this.props
return (
<div>
<Picker value={selectedSubreddit}
onChange={this.handleChange}
options={[ 'reactjs', 'frontend' ]} />
<p>
{lastUpdated &&
<span>
Last updated at {new Date(lastUpdated).toLocaleTimeString()}.
{' '}
</span>
}
{!isFetching &&
<a href='#'
onClick={this.handleRefreshClick}>
Refresh
</a>
}
</p>
{isFetching && posts.length === 0 &&
<h2>Loading...</h2>
}
{!isFetching && posts.length === 0 &&
<h2>Empty.</h2>
}
{posts.length > 0 &&
<div style={{ opacity: isFetching ? 0.5 : 1 }}>
<Posts posts={posts} />
</div>
}
</div>
)
}
}
AsyncApp.propTypes = {
selectedSubreddit: PropTypes.string.isRequired,
posts: PropTypes.array.isRequired,
isFetching: PropTypes.bool.isRequired,
lastUpdated: PropTypes.number,
dispatch: PropTypes.func.isRequired
}
function mapStateToProps(state) {
const { selectedSubreddit, postsBySubreddit } = state
const {
isFetching,
lastUpdated,
121
Example: Reddit API
items: posts
} = postsBySubreddit[selectedSubreddit] || {
isFetching: true,
items: []
}
return {
selectedSubreddit,
posts,
isFetching,
lastUpdated
}
}
Presentational Components
components/Picker.js
return (
<span>
<h1>{value}</h1>
<select onChange={e => onChange(e.target.value)}
value={value}>
{options.map(option =>
<option value={option} key={option}>
{option}
</option>)
}
</select>
</span>
)
}
}
Picker.propTypes = {
options: PropTypes.arrayOf(
PropTypes.string.isRequired
).isRequired,
value: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired
}
122
Example: Reddit API
components/Posts.js
Posts.propTypes = {
posts: PropTypes.array.isRequired
}
123
Recipes
Recipes
These are some use cases and code snippets to get you started with Redux in a real
app. They assume you understand the topics in basic and advanced tutorials.
Migrating to Redux
Using Object Spread Operator
Reducing Boilerplate
Server Rendering
Writing Tests
Computing Derived Data
Implementing Undo History
Isolating Subapps
124
Migrating to Redux
Migrating to Redux
Redux is not a monolithic framework, but a set of contracts and a few functions that
make them work together. The majority of your Redux code will not even use Redux
APIs, as most of the time you'll be writing functions.
From Flux
Reducers capture the essence of Flux Stores, so it's possible to gradually migrate an
existing Flux project towards Redux, whether you are using Flummox, Alt, traditional
Flux, or any other Flux library.
It is also possible to do the reverse and migrate from Redux to any of these libraries
following the same steps.
This allows you to gradually rewrite every Flux Store in your app as a reducer, but
still export createFluxStore(reducer) so the rest of your app is not aware that this is
happening and sees the Flux stores.
As you rewrite your Stores, you will find that you need to avoid certain Flux anti-
patterns such as fetching API inside the Store, or triggering actions inside the
Stores. Your Flux code will be easier to follow once you port it to be based on
reducers!
When you have ported all of your Flux Stores to be implemented on top of reducers,
you can replace the Flux library with a single Redux store, and combine those
reducers you already have into one using combineReducers(reducers) .
125
Migrating to Redux
Finally, you might want to begin using some Redux idioms like middleware to further
simplify your asynchronous code.
From Backbone
Backbone's model layer is quite different from Redux, so we don't suggest mixing them.
If possible, it is best that you rewrite your app's model layer from scratch instead of
connecting Backbone to Redux. However, if a rewrite is not feasible, you may use
backbone-redux to migrate gradually, and keep the Redux store in sync with Backbone
models and collections.
126
Using Object Spread Operator
While effective, using Object.assign() can quickly make simple reducers difficult to
read given its rather verbose syntax.
An alternative approach is to use the object spread syntax proposed for the next
versions of JavaScript which lets you use the spread ( ... ) operator to copy
enumerable properties from one object to another in a more succinct way. The object
spread operator is conceptually similar to the ES6 array spread operator. We can
simplify the todoApp example above by using the object spread syntax:
The advantage of using the object spread syntax becomes more apparent when you're
composing complex objects. Below getAddedIds maps an array of id values to an
array of objects with values returned from getProduct and getQuantity .
127
Using Object Spread Operator
Since the object spread syntax is still a Stage 3 proposal for ECMAScript you'll need to
use a transpiler such as Babel to use it in production. You can use your existing es2015
preset, install babel-plugin-transform-object-rest-spread and add it individually to the
plugins array in your .babelrc .
{
"presets": ["es2015"],
"plugins": ["transform-object-rest-spread"]
}
Note that this is still an experimental language feature proposal so it may change in the
future. Nevertheless some large projects such as React Native already use it extensively
so it is safe to say that there will be a good automated migration path if it changes.
128
Reducing Boilerplate
Reducing Boilerplate
Redux is in part inspired by Flux, and the most common complaint about Flux is how it
makes you write a lot of boilerplate. In this recipe, we will consider how Redux lets us
choose how verbose we'd like our code to be, depending on personal style, team
preferences, longer term maintainability, and so on.
Actions
Actions are plain objects describing what happened in the app, and serve as the sole
way to describe an intention to mutate the data. It's important that actions being
objects you have to dispatch is not boilerplate, but one of the fundamental design
choices of Redux.
There are frameworks claiming to be similar to Flux, but without a concept of action
objects. In terms of being predictable, this is a step backwards from Flux or Redux. If
there are no serializable plain object actions, it is impossible to record and replay user
sessions, or to implement hot reloading with time travel. If you'd rather modify data
directly, you don't need Redux.
It is a common convention that actions have a constant type that helps reducers (or
Stores in Flux) identify them. We recommend that you use strings and not Symbols for
action types, because strings are serializable, and by using Symbols you make
recording and replaying harder than it needs to be.
In Flux, it is traditionally thought that you would define every action type as a string
constant:
129
Reducing Boilerplate
Why is this beneficial? It is often claimed that constants are unnecessary, and for
small projects, this might be correct. For larger projects, there are some benefits to
defining action types as constants:
It helps keep the naming consistent because all action types are gathered in a
single place.
Sometimes you want to see all existing actions before working on a new feature. It
may be that the action you need was already added by somebody on the team, but
you didn't know.
The list of action types that were added, removed, and changed in a Pull Request
helps everyone on the team keep track of scope and implementation of new
features.
If you make a typo when importing an action constant, you will get undefined .
Redux will immediately throw when dispatching such an action, and you'll find the
mistake sooner.
It is up to you to choose the conventions for your project. You may start by using inline
strings, and later transition to constants, and maybe later group them into a single file.
Redux does not have any opinion here, so use your best judgment.
Action Creators
It is another common convention that, instead of creating action objects inline in the
places where you dispatch the actions, you would create functions generating them.
You might write an action creator in a separate file, and import it from your component:
actionCreators.js
130
Reducing Boilerplate
AddTodo.js
Action creators have often been criticized as boilerplate. Well, you don't have to write
them! You can use object literals if you feel this better suits your project. There
are, however, some benefits for writing action creators you should know about.
Let's say a designer comes back to us after reviewing our prototype, and tells that we
need to allow three todos maximum. We can enforce this by rewriting our action creator
to a callback form with redux-thunk middleware and adding an early exit:
function addTodoWithoutCheck(text) {
return {
type: 'ADD_TODO',
text
}
}
dispatch(addTodoWithoutCheck(text))
}
}
We just modified how the addTodo action creator behaves, completely invisible to the
calling code. We don't have to worry about looking at each place where todos are
being added, to make sure they have this check. Action creators let you decouple
131
Reducing Boilerplate
additional logic around dispatching an action, from the actual components emitting those
actions. It's very handy when the application is under heavy development, and the
requirements change often.
generate action type constants, but they're created implicitly so it's a level of indirection
and can cause confusion. We recommend creating your action type constants explicitly.
Writing simple action creators can be tiresome and often ends up generating redundant
boilerplate code:
132
Reducing Boilerplate
There are also utility libraries to aid in generating action creators, such as redux-act and
redux-actions. These can help reduce boilerplate code and enforce adherence to
standards such as Flux Standard Action (FSA).
Without any middleware, dispatch only accepts a plain object, so we have to perform
AJAX calls inside our components:
actionCreators.js
133
Reducing Boilerplate
UserInfo.js
134
Reducing Boilerplate
if (posts[userId]) {
// There is cached data! Don't do anything.
return
}
componentDidMount() {
this.loadData(this.props.userId)
}
componentWillReceiveProps(nextProps) {
if (nextProps.userId !== this.props.userId) {
this.loadData(nextProps.userId)
}
}
render() {
if (this.props.isFetching) {
return <p>Loading...</p>
}
return <div>{posts}</div>
}
}
135
Reducing Boilerplate
However, this quickly gets repetitive because different components request data from
the same API endpoints. Moreover, we want to reuse some of this logic (e.g., early exit
when there is cached data available) from many components.
Middleware lets us write more expressive, potentially async action creators. It lets
us dispatch something other than plain objects, and interprets the values. For example,
middleware can catch dispatched Promises and turn them into a pair of request and
success/failure actions.
Note
actionCreators.js
136
Reducing Boilerplate
dispatch({
type: 'LOAD_POSTS_REQUEST',
userId
})
UserInfo.js
137
Reducing Boilerplate
componentWillReceiveProps(nextProps) {
if (nextProps.userId !== this.props.userId) {
this.props.dispatch(loadPosts(nextProps.userId))
}
}
render() {
if (this.props.isFetching) {
return <p>Loading...</p>
}
return <div>{posts}</div>
}
}
This is much less typing! If you'd like, you can still have vanilla action creators like
loadPostsSuccess which you'd use from a container loadPosts action creator.
Finally, you can write your own middleware. Let's say you want to generalize the
pattern above and describe your async action creators like this instead:
138
Reducing Boilerplate
The middleware that interprets such actions could look like this:
if (!types) {
// Normal action: pass it on
return next(action)
}
if (
!Array.isArray(types) ||
types.length !== 3 ||
!types.every(type => typeof type === 'string')
) {
throw new Error('Expected an array of three string types.')
}
if (!shouldCallAPI(getState())) {
return
}
dispatch(Object.assign({}, payload, {
type: requestType
}))
return callAPI().then(
response => dispatch(Object.assign({}, payload, {
response,
type: successType
})),
error => dispatch(Object.assign({}, payload, {
error,
type: failureType
}))
)
}
}
After passing it once to applyMiddleware(...middlewares) , you can write all your API-
calling action creators the same way:
139
Reducing Boilerplate
Reducers
Redux reduces the boilerplate of Flux stores considerably by describing the update logic
as a function. A function is simpler than an object, and much simpler than a class.
140
Reducing Boilerplate
let _todos = []
AppDispatcher.register(function (action) {
switch (action.type) {
case ActionTypes.ADD_TODO:
let text = action.text.trim()
_todos.push(text)
TodoStore.emitChange()
}
})
With Redux, the same update logic can be described as a reducing function:
The switch statement is not the real boilerplate. The real boilerplate of Flux is
conceptual: the need to emit an update, the need to register the Store with a Dispatcher,
the need for the Store to be an object (and the complications that arise when you want a
universal app).
It's unfortunate that many still choose Flux framework based on whether it uses switch
statements in the documentation. If you don't like switch , you can solve this with a
single function, as we show below.
Generating Reducers
Let's write a function that lets us express reducers as an object mapping from action
types to handlers. For example, if we want our todos reducers to be defined like this:
141
Reducing Boilerplate
This wasn't difficult, was it? Redux doesn't provide such a helper function by default
because there are many ways to write it. Maybe you want it to automatically convert
plain JS objects to Immutable objects to hydrate the server state. Maybe you want to
merge the returned state with the current state. There may be different approaches to a
catch all handler. All of this depends on the conventions you choose for your team on a
specific project.
The Redux reducer API is (state, action) => state , but how you create those
reducers is up to you.
142
Server Rendering
Server Rendering
The most common use case for server-side rendering is to handle the initial render when
a user (or search engine crawler) first requests our app. When the server receives the
request, it renders the required component(s) into an HTML string, and then sends it as
a response to the client. From that point on, the client takes over rendering duties.
We will use React in the examples below, but the same techniques can be used with
other view frameworks that can render on the server.
On the client side, a new Redux store will be created and initialized with the state
provided from the server.
Redux's only job on the server side is to provide the initial state of our app.
Setting Up
In the following recipe, we are going to look at how to set up server-side rendering. We'll
use the simplistic Counter app as a guide and show how the server can render state
ahead of time based on the request.
Install Packages
143
Server Rendering
For this example, we'll be using Express as a simple web server. We also need to install
the React bindings for Redux, since they are not included in Redux by default.
server.js
app.listen(port)
When rendering, we will wrap <App /> , our root component, inside a <Provider> to
make the store available to all components in the component tree, as we saw in Usage
with React.
144
Server Rendering
The key step in server side rendering is to render the initial HTML of our component
before we send it to the client side. To do this, we use
ReactDOMServer.renderToString().
We then get the initial state from our Redux store using store.getState() . We will see
how this is passed along in our renderFullPage function.
We also include our bundle file for the client-side application via a script tag. This is
whatever output your bundling tool provides for your client entry point. It may be a static
file or a URL to a hot reloading development server.
145
Server Rendering
state.
client.js
146
Server Rendering
// Grab the state from a global variable injected into the server-generated HTML
const preloadedState = window.__PRELOADED_STATE__
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
You can set up your build tool of choice (Webpack, Browserify, etc.) to compile a bundle
file into static/bundle.js .
When the page loads, the bundle file will be started up and ReactDOM.render() will hook
into the data-react-id attributes from the server-rendered HTML. This will connect our
newly-started React instance to the virtual DOM used on the server. Since we have the
same initial state for our Redux store and used the same code for all our view
components, the result will be the same real DOM.
And that's it! That is all we need to do to implement server side rendering.
But the result is pretty vanilla. It essentially renders a static view from dynamic code.
What we need to do next is build an initial state dynamically to allow that rendered view
to be dynamic.
147
Server Rendering
The only input for server side code is the request made when loading up a page in your
app in your browser. You may choose to configure the server during its boot (such as
when you are running in a development vs. production environment), but that
configuration is static.
The request contains information about the URL requested, including any query
parameters, which will be useful when using something like React Router. It can also
contain headers with inputs like cookies or authorization, or POST body data. Let's see
how we can set the initial counter state based on a query parameter.
server.js
The code reads from the Express Request object passed into our server middleware.
The parameter is parsed into a number and then set in the initial state. If you visit
http://localhost:3000/?counter=100 in your browser, you'll see the counter starts at 100.
In the rendered HTML, you'll see the counter output as 100 and the
__PRELOADED_STATE__ variable has the counter set in it.
148
Server Rendering
The easiest way to do this is to pass through some callback back to your synchronous
code. In this case, that will be a function that will reference the response object and send
back our rendered HTML to the client. Don't worry, it's not as hard as it may sound.
For our example, we'll imagine there is an external datastore that contains the counter's
initial value (Counter As A Service, or CaaS). We'll make a mock call over to them and
build our initial state from the result. We'll start by building out our API call:
api/counter.js
Again, this is just a mock API, so we use setTimeout to simulate a network request that
takes 500 milliseconds to respond (this should be much faster with a real world API). We
pass in a callback that returns a random number asynchronously. If you're using a
Promise-based API client, then you would issue this callback in your then handler.
On the server side, we simply wrap our existing code in the fetchCounter and receive
the result in the callback:
server.js
149
Server Rendering
Because we call res.send() inside of the callback, the server will hold open the
connection and won't send any data until that callback executes. You'll notice a 500ms
delay is now added to each server request as a result of our new API call. A more
advanced usage would handle errors in the API gracefully, such as a bad response or
timeout.
Security Considerations
Because we have introduced more code that relies on user generated content (UGC)
and input, we have increased our attack surface area for our application. It is important
for any application that you ensure your input is properly sanitized to prevent things like
cross-site scripting (XSS) attacks or code injections.
150
Server Rendering
rendered HTML by providing a script tag in the request. That might look like this: ?
counter=</script><script>doSomethingBad();</script>
For our simplistic example, coercing our input into a number is sufficiently secure. If
you're handling more complex input, such as freeform text, then you should run that
input through an appropriate sanitization function, such as validator.js.
Furthermore, you can add additional layers of security by sanitizing your state output.
JSON.stringify can be subject to script injections. To counter this, you can scrub the
JSON string of HTML tags and other dangerous characters. This can be done with either
a simple text replacement on the string, e.g. JSON.stringify(state).replace(/</g,
'\\u003c') , or via more sophisticated libraries such as serialize-javascript.
Next Steps
You may want to read Async Actions to learn more about expressing asynchronous flow
in Redux with async primitives such as Promises and thunks. Keep in mind that anything
you learn there can also be applied to universal rendering.
If you use something like React Router, you might also want to express your data
fetching dependencies as static fetchData() methods on your route handler
components. They may return async actions, so that your handleRender function can
match the route to the route handler component classes, dispatch fetchData() result
for each of them, and render only after the Promises have resolved. This way the
specific API calls required for different routes are colocated with the route handler
component definitions. You can also use the same technique on the client side to
prevent the router from switching the page until its data has been loaded.
151
Writing Tests
Writing Tests
Because most of the Redux code you write are functions, and many of them are pure,
they are easy to test without mocking.
Setting Up
We recommend Jest as the testing engine. Note that it runs in a Node environment, so
you won't have access to the DOM.
{
"presets": ["es2015"]
}
{
...
"scripts": {
...
"test": "jest",
"test:watch": "npm test -- --watch"
},
...
}
and run npm test to run it once, or npm run test:watch to test on every file change.
Action Creators
152
Writing Tests
In Redux, action creators are functions which return plain objects. When testing action
creators we want to test whether the correct action creator was called and also whether
the right action was returned.
Example
describe('actions', () => {
it('should create an action to add a todo', () => {
const text = 'Finish docs'
const expectedAction = {
type: types.ADD_TODO,
text
}
expect(actions.addTodo(text)).toEqual(expectedAction)
})
})
Example
153
Writing Tests
function fetchTodosRequest() {
return {
type: FETCH_TODOS_REQUEST
}
}
function fetchTodosSuccess(body) {
return {
type: FETCH_TODOS_SUCCESS,
body
}
}
function fetchTodosFailure(ex) {
return {
type: FETCH_TODOS_FAILURE,
ex
}
}
154
Writing Tests
const expectedActions = [
{ type: types.FETCH_TODOS_REQUEST },
{ type: types.FETCH_TODOS_SUCCESS, body: { todos: ['do something'] } }
]
const store = mockStore({ todos: [] })
return store.dispatch(actions.fetchTodos())
.then(() => { // return of async actions
expect(store.getActions()).toEqual(expectedActions)
})
})
})
Reducers
A reducer should return the new state after applying the action to the previous state, and
that's the behavior tested below.
Example
155
Writing Tests
const initialState = [
{
text: 'Use Redux',
completed: false,
id: 0
}
]
default:
return state
}
}
156
Writing Tests
{
text: 'Run the tests',
completed: false,
id: 0
}
]
)
expect(
reducer(
[
{
text: 'Use Redux',
completed: false,
id: 0
}
],
{
type: types.ADD_TODO,
text: 'Run the tests'
}
)
).toEqual(
[
{
text: 'Run the tests',
completed: false,
id: 1
},
{
text: 'Use Redux',
completed: false,
id: 0
}
]
)
})
})
Components
A nice thing about React components is that they are usually small and only rely on their
props. That makes them easy to test.
First, we will install Enzyme. Enzyme uses the React Test Utilities underneath, but is
more convenient, readable, and powerful.
157
Writing Tests
To test the components we make a setup() helper that passes the stubbed callbacks
as props and renders the component with shallow rendering. This lets individual tests
assert on whether the callbacks were called when expected.
Example
render() {
return (
<header className='header'>
<h1>todos</h1>
<TodoTextInput newTodo={true}
onSave={this.handleSave.bind(this)}
placeholder='What needs to be done?' />
</header>
)
}
}
Header.propTypes = {
addTodo: PropTypes.func.isRequired
}
158
Writing Tests
function setup() {
const props = {
addTodo: jest.fn()
}
return {
props,
enzymeWrapper
}
}
describe('components', () => {
describe('Header', () => {
it('should render self and subcomponents', () => {
const { enzymeWrapper } = setup()
expect(enzymeWrapper.find('header').hasClass('header')).toBe(true)
expect(enzymeWrapper.find('h1').text()).toBe('todos')
Connected Components
If you use a library like React Redux, you might be using higher-order components like
connect() . This lets you inject Redux state into a regular React component.
159
Writing Tests
In a unit test, you would normally import the App component like this:
However, when you import it, you're actually holding the wrapper component returned by
connect() , and not the App component itself. If you want to test its interaction with
Redux, this is good news: you can wrap it in a <Provider> with a store created
specifically for this unit test. But sometimes you want to test just the rendering of the
component, without a Redux store.
In order to be able to test the App component itself without having to deal with the
decorator, we recommend you to also export the undecorated component:
Since the default export is still the decorated component, the import statement pictured
above will work as before so you won't have to change your application code. However,
you can now import the undecorated App components in your test file like this:
// Note the curly braces: grab the named export instead of default export
import { App } from './App'
160
Writing Tests
If you are using ES6 in your application source, but write your tests in ES5, you
should know that Babel handles the interchangeable use of ES6 import and
CommonJS require through its interop capability to run two module formats
side-by-side, but the behavior is slightly different. If you add a second export
beside your default export, you can no longer import the default using
require('./App') . Instead you have to use require('./App').default .
Middleware
Middleware functions wrap behavior of dispatch calls in Redux, so to test this modified
behavior we need to mock the behavior of the dispatch call.
Example
161
Writing Tests
describe('middleware', () => {
it('should dispatch if store is empty', () => {
const action = {
type: types.ADD_TODO
}
expect(
dispatchWithStoreOf({}, action)
).toEqual(action)
})
expect(
dispatchWithStoreOf({
[types.ADD_TODO]: 'dispatched'
}, action)
).toNotExist()
})
})
Glossary
Enzyme: Enzyme is a JavaScript Testing utility for React that makes it easier to
assert, manipulate, and traverse your React Components' output.
162
Writing Tests
tests, where you test a particular component only, and importantly not its children.
This also means that changing a child component won't affect the tests for the
parent component. Testing a component and all its children can be accomplished
with Enzyme's mount() method, aka full DOM rendering.
163
Computing Derived Data
containers/VisibleTodoList.js
164
Computing Derived Data
arguments. If the Redux state tree is mutated in a way that causes the value of an input-
selector to change, the selector will call its transform function with the values of the
input-selectors as arguments and return the result. If the values of the input-selectors
are the same as the previous call to the selector, it will return the previously computed
value instead of calling the transform function.
selectors/index.js
165
Computing Derived Data
Composing Selectors
A memoized selector can itself be an input-selector to another memoized selector. Here
is getVisibleTodos being used as an input-selector to a selector that further filters the
todos by keyword:
containers/VisibleTodoList.js
166
Computing Derived Data
So far we have only seen selectors receive the Redux store state as an argument, but a
selector can receive props too.
components/App.js
167
Computing Derived Data
Each VisibleTodoList container should select a different slice of the state depending on
the value of the listId prop, so let's modify getVisibilityFilter and getTodos to
accept a props argument:
selectors/todoSelectors.js
168
Computing Derived Data
containers/VisibleTodoList.js
A selector created with createSelector only returns the cached value when its set of
arguments is the same as its previous set of arguments. If we alternate between
rendering <VisibleTodoList listId="1" /> and <VisibleTodoList listId="2" /> , the
shared selector will alternate between receiving {listId: 1} and {listId: 2} as its
169
Computing Derived Data
props argument. This will cause the arguments to be different on each call, so the
selector will always recompute instead of returning the cached value. We'll see how to
overcome this limitation in the next section.
Let's create a function named makeGetVisibleTodos that returns a new copy of the
getVisibleTodos selector each time it is called:
selectors/todoSelectors.js
We also need a way to give each instance of a container access to its own private
selector. The mapStateToProps argument of connect can help with this.
170
Computing Derived Data
containers/VisibleTodoList.js
171
Computing Derived Data
Next Steps
Check out the official documentation of Reselect as well as its FAQ. Most Redux
projects start using Reselect when they have performance problems because of too
many derived computations and wasted re-renders, so make sure you are familiar with it
before you build something big. It can also be useful to study its source code so you
don't think it's magic.
172
Implementing Undo History
This means that implementing Undo and Redo in an MVC application usually forces you
to rewrite parts of your application to use a specific data mutation pattern like Command.
With Redux, however, implementing undo history is a breeze. There are three reasons
for this:
There are no multiple modelsjust a state subtree that you want to keep track of.
The state is already immutable, and mutations are already described as discrete
actions, which is close to the undo stack mental model.
The reducer (state, action) => state signature makes it natural to implement
generic reducer enhancers or higher order reducers. They are functions that take
your reducer and enhance it with some additional functionality while preserving its
signature. Undo history is exactly such a case.
Before proceeding, make sure you have worked through the basics tutorial and
understand reducer composition well. This recipe will build on top of the example
described in the basics tutorial.
In the first part of this recipe, we will explain the underlying concepts that make Undo
and Redo possible to implement in a generic way.
In the second part of this recipe, we will show how to use Redux Undo package that
provides this functionality out of the box.
173
Implementing Undo History
For example, the state shape of a counter app might look like this:
{
counter: 10
}
If we wanted to implement Undo and Redo in such an app, we'd need to store more
state so we can answer the following questions:
It is reasonable to suggest that our state shape should change to answer these
questions:
174
Implementing Undo History
{
counter: {
past: [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ],
present: 10,
future: []
}
}
Now, if user presses Undo, we want it to change to move into the past:
{
counter: {
past: [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ],
present: 9,
future: [ 10 ]
}
}
{
counter: {
past: [ 0, 1, 2, 3, 4, 5, 6, 7 ],
present: 8,
future: [ 9, 10 ]
}
}
When the user presses Redo, we want to move one step back into the future:
{
counter: {
past: [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ],
present: 9,
future: [ 10 ]
}
}
Finally, if the user performs an action (e.g. decrement the counter) while we're in the
middle of the undo stack, we're going to discard the existing future:
175
Implementing Undo History
{
counter: {
past: [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ],
present: 8,
future: []
}
}
The interesting part here is that it does not matter whether we want to keep an undo
stack of numbers, strings, arrays, or objects. The structure will always be the same:
{
counter: {
past: [ 0, 1, 2 ],
present: 3,
future: [ 4 ]
}
}
{
todos: {
past: [
[],
[ { text: 'Use Redux' } ],
[ { text: 'Use Redux', complete: true } ]
],
present: [ { text: 'Use Redux', complete: true }, { text: 'Implement Undo' } ],
future: [
[ { text: 'Use Redux', complete: true }, { text: 'Implement Undo', complete:
true } ]
]
}
}
{
past: Array<T>,
present: T,
future: Array<T>
}
176
Implementing Undo History
{
past: [
{ counterA: 1, counterB: 1 },
{ counterA: 1, counterB: 0 },
{ counterA: 0, counterB: 0 }
],
present: { counterA: 2, counterB: 1 },
future: []
}
Or many granular histories so user can undo and redo actions in them independently:
{
counterA: {
past: [ 1, 0 ],
present: 2,
future: []
},
counterB: {
past: [ 0 ],
present: 1,
future: []
}
}
We will see later how the approach we take lets us choose how granular Undo and Redo
need to be.
{
past: Array<T>,
present: T,
future: Array<T>
}
Let's talk through the algorithm to manipulate the state shape described above. We can
define two actions to operate on this state: UNDO and REDO . In our reducer, we will do
the following steps to handle these actions:
Handling Undo
Remove the last element from the past .
177
Implementing Undo History
Handling Redo
Remove the first element from the future .
Set the present to the element we removed in the previous step.
Insert the old present state at the end of the past .
178
Implementing Undo History
const initialState = {
past: [],
present: null, // (?) How do we initialize the present?
future: []
}
switch (action.type) {
case 'UNDO':
const previous = past[past.length - 1]
const newPast = past.slice(0, past.length - 1)
return {
past: newPast,
present: previous,
future: [ present, ...future ]
}
case 'REDO':
const next = future[0]
const newFuture = future.slice(1)
return {
past: [ ...past, present ],
present: next,
future: newFuture
}
default:
// (?) How do we handle other actions?
return state
}
}
This implementation isn't usable because it leaves out three important questions:
Where do we get the initial present state from? We don't seem to know it
beforehand.
Where do we react to the external actions to save the present to the past ?
How do we actually delegate the control over the present state to a custom
reducer?
It seems that reducer isn't the right abstraction, but we're very close.
179
Implementing Undo History
A reducer enhancer (or a higher order reducer) is a function that takes a reducer, and
returns a new reducer that is able to handle new actions, or to hold more state,
delegating control to the inner reducer for the actions it doesn't understand. This isn't a
new patterntechnically, combineReducers() is also a reducer enhancer because it
takes reducers and returns a new reducer.
function doNothingWith(reducer) {
return function (state, action) {
// Just call the passed reducer
return reducer(state, action)
}
}
A reducer enhancer that combines other reducers might look like this:
function combineReducers(reducers) {
return function (state = {}, action) {
return Object.keys(reducers).reduce((nextState, key) => {
// Call every reducer with the part of the state it manages
nextState[key] = reducers[key](state[key], action)
return nextState
}, {})
}
}
180
Implementing Undo History
function undoable(reducer) {
// Call the reducer with empty action to populate the initial state
const initialState = {
past: [],
present: reducer(undefined, {}),
future: []
}
switch (action.type) {
case 'UNDO':
const previous = past[past.length - 1]
const newPast = past.slice(0, past.length - 1)
return {
past: newPast,
present: previous,
future: [ present, ...future ]
}
case 'REDO':
const next = future[0]
const newFuture = future.slice(1)
return {
past: [ ...past, present ],
present: next,
future: newFuture
}
default:
// Delegate handling the action to the passed reducer
const newPresent = reducer(present, action)
if (present === newPresent) {
return state
}
return {
past: [ ...past, present ],
present: newPresent,
future: []
}
}
}
}
We can now wrap any reducer into undoable reducer enhancer to teach it to react to
UNDO and REDO actions.
181
Implementing Undo History
// This is a reducer
function todos(state = [], action) {
/* ... */
}
store.dispatch({
type: 'ADD_TODO',
text: 'Use Redux'
})
store.dispatch({
type: 'ADD_TODO',
text: 'Implement Undo'
})
store.dispatch({
type: 'UNDO'
})
There is an important gotcha: you need to remember to append .present to the current
state when you retrieve it. You may also check .past.length and .future.length to
determine whether to enable or to disable the Undo and Redo buttons, respectively.
You might have heard that Redux was influenced by Elm Architecture. It shouldn't come
as a surprise that this example is very similar to elm-undo-redo package.
In this part of the recipe, you will learn how to make the Todo List example undoable.
You can find the full source of this recipe in the todos-with-undo example that comes
with Redux.
Installation
First of all, you need to run
182
Implementing Undo History
This installs the package that provides the undoable reducer enhancer.
reducers/todos.js
/* ... */
The distinctState() filter serves to ignore the actions that didn't result in a state
change. There are many other options to configure your undoable reducer, like setting
the action type for Undo and Redo actions.
Note that your combineReducers() call will stay exactly as it was, but the todos reducer
will now refer to the reducer enhanced with Redux Undo:
reducers/index.js
183
Implementing Undo History
You may wrap one or more reducers in undoable at any level of the reducer
composition hierarchy. We choose to wrap todos instead of the top-level combined
reducer so that changes to visibilityFilter are not reflected in the undo history.
{
visibilityFilter: 'SHOW_ALL',
todos: {
past: [
[],
[ { text: 'Use Redux' } ],
[ { text: 'Use Redux', complete: true } ]
],
present: [ { text: 'Use Redux', complete: true }, { text: 'Implement Undo' } ],
future: [
[ { text: 'Use Redux', complete: true }, { text: 'Implement Undo', complete:
true } ]
]
}
}
This means you need to access your state with state.todos.present instead of just
state.todos :
containers/VisibleTodoList.js
184
Implementing Undo History
First, create a new container component called UndoRedo for these buttons. We won't
bother to split the presentational part into a separate file because it is very small:
containers/UndoRedo.js
/* ... */
You will use connect() from React Redux to generate a container component. To
determine whether to enable Undo and Redo buttons, you can check
state.todos.past.length and state.todos.future.length . You won't need to write
action creators for performing undo and redo because Redux Undo already provides
them:
containers/UndoRedo.js
185
Implementing Undo History
/* ... */
/* ... */
UndoRedo = connect(
mapStateToProps,
mapDispatchToProps
)(UndoRedo)
components/App.js
This is it! Run npm install and npm start in the example folder and try it out!
186
Implementing Undo History
187
Isolating Subapps
These <SubApp> s will be completely independent. They won't share data or actions, and
won't see or communicate with each other.
It's best not to mix this approach with standard Redux reducer composition. For typical
web apps, stick with reducer composition. For product hubs, dashboards, or
enterprise software that groups disparate tools into a unified package, give the sub-app
approach a try.
The sub-app approach is also useful for large teams that are divided by product or
feature verticals. These teams can ship sub-apps independently or in combination with
an enclosing app shell.
188
Isolating Subapps
Maybe we want to be able to run multiple instances of it in the same bigger app and
keep it as a complete black box, with Redux being an implementation detail.
To hide Redux behind a React API, we can wrap it in a special component that initializes
the store in the constructor:
render() {
return (
<Provider store={this.store}>
<App />
</Provider>
)
}
}
This pattern is not recommended for parts of the same app that share data. However, it
can be useful when the bigger app has zero access to the smaller apps' internals, and
we'd like to keep the fact that they are implemented with Redux as an implementation
detail. Each component instance will have its own store, so they won't know about
each other.
189
Structuring Reducers
Structuring Reducers
At its core, Redux is really a fairly simple design pattern: all your "write" logic goes into a
single function, and the only way to run that logic is to give Redux a plain object that
describes something that has happened. The Redux store calls that write logic function
and passes in the current state tree and the descriptive object, the write logic function
returns some new state tree, and the Redux store notifies any subscribers that the state
tree has changed.
Redux puts some basic constraints on how that write logic function should work. As
described in Reducers, it has to have a signature of (previousState, action) =>
newState , is known as a reducer function, and must be pure and predictable.
Beyond that, Redux does not really care how you actually structure your logic inside that
reducer function, as long as it obeys those basic rules. This is both a source of freedom
and a source of confusion. However, there are a number of common patterns that are
widely used when writing reducers, as well as a number of related topics and concepts
to be aware of. As an application grows, these patterns play a crucial role in managing
reducer code complexity, handling real-world data, and optimizing UI performance.
It is vital that these Prerequisite Concepts are thoroughly understood before moving
on to more advanced and Redux-specific techniques. A recommended reading list is
available at:
Prerequisite Concepts
It's also important to note that some of these suggestions may or may not be directly
applicable based on architectural decisions in a specific application. For example, an
application using Immutable.js Maps to store data would likely have its reducer logic
190
Structuring Reducers
191
Prerequisite Concepts
Should have a signature of (previousState, action) => newState , similar to the type
of function you would pass to Array.prototype.reduce(reducer, ?initialValue)
Should be "pure", which means it does not mutate its arguments, perform side
effects like API calls or modifying values outside of the function, or call non-pure
functions like Date.now() or Math.random() . This also means that updates should
be done in an "immutable" fashion, which means always returning new objects
with the updated data, rather than directly modifying the original state tree in-
place.
For time traveling, the Redux DevTools expect that replaying recorded
actions would output a state value, but not change anything else. Side
effects like mutation or asynchronous behavior will cause time travel to
alter behavior between steps, breaking the application.
For React Redux, connect checks to see if the props returned from a
mapStateToProps function have changed in order to determine if a component
Other side effects like generating unique IDs or timestamps in a reducer also
make the code unpredictable and harder to debug and test.
Because of these rules, it's important that the following core concepts are fully
understood before moving on to other specific techniques for organizing Redux
reducers:
192
Prerequisite Concepts
Key concepts:
Reading list:
Side effects
Pure functions
How to think in terms of combining functions
Reading List:
Mutability vs immutability
Immutably updating objects and arrays safely
Avoiding functions and statements that mutate state
Reading List:
193
Prerequisite Concepts
Normalizing Data
Key Concepts:
Reading List:
194
Basic Reducer Structure
The first time the reducer is called, the state value will be undefined . The reducer
needs to handle this case by supplying a default state value before handling the
incoming action.
It needs to look at the previous state and the dispatched action, and determine what
kind of work needs to be done
Assuming actual changes need to occur, it needs to create new objects and arrays
with the updated data and return those
If no changes are needed, it should return the existing state as-is.
The simplest possible approach to writing reducer logic is to put everything into a single
function declaration, like this:
195
Basic Reducer Structure
Notice that this simple function fulfills all the basic requirements. It returns a default
value if none exists, initializing the store; it determines what sort of update needs to be
done based on the type of the action, and returns new values; and it returns the previous
state if no work needs to be done.
There are some simple tweaks that can be made to this reducer. First, repeated
if / else statements quickly grow tiresome, so it's very common to use switch
statements instead. Second, we can use ES6's default parameter values to handle the
initial "no existing data" case. With those changes, the reducer would look like:
This is the basic structure that a typical Redux reducer function uses.
A Redux state usually has a plain Javascript object as the top of the state tree. (It is
certainly possible to have another type of data instead, such as a single number, an
array, or a specialized data structure, but most libraries assume that the top-level value
is a plain object.) The most common way to organize data within that top-level object is
to further divide data into sub-trees, where each top-level key represents some "domain"
or "slice" of related data. For example, a basic Todo app's state might look like:
196
Basic Reducer Structure
{
visibilityFilter: 'SHOW_ALL',
todos: [
{
text: 'Consider using Redux',
completed: true,
},
{
text: 'Keep all state in a single tree',
completed: false
}
]
}
In this example, todos and visibilityFilter are both top-level keys in the state, and
each represents a "slice" of data for some particular concept.
Most applications deal with multiple types of data, which can be broadly divided into
three categories:
Domain data: data that the application needs to show, use, or modify (such as "all of
the Todos retrieved from the server")
App state: data that is specific to the application's behavior (such as "Todo #5 is
currently selected", or "there is a request in progress to fetch Todos")
UI state: data that represents how the UI is currently displayed (such as "The
EditTodo modal dialog is currently open")
Because the store represents the core of your application, you should define your state
shape in terms of your domain data and app state, not your UI component tree. As
an example, a shape of state.leftPane.todoList.todos would be a bad idea, because
the idea of "todos" is central to the whole application, not just a single part of the UI. The
todos slice should be at the top of the state tree instead.
There will rarely be a 1-to-1 correspondence between your UI tree and your state shape.
The exception to that might be if you are explicitly tracking various aspects of UI data in
your Redux store as well, but even then the shape of the UI data and the shape of the
domain data would likely be different.
197
Basic Reducer Structure
{
domainData1 : {},
domainData2 : {},
appState1 : {},
appState2 : {},
ui : {
uiState1 : {},
uiState2 : {},
}
}
198
Splitting Reducer Logic
Since a Redux reducer is just a function, the same concept applies. You can split some
of your reducer logic out into another function, and call that new function from the parent
function.
These new functions would typically fall into one of three categories:
1. Small utility functions containing some reusable chunk of logic that is needed in
multiple places (which may or may not be actually related to the specific business
logic)
2. Functions for handling a specific update case, which often need parameters other
than the typical (state, action) pair
3. Functions which handle all updates for a given slice of state. These functions do
generally have the typical (state, action) parameter signature
For clarity, these terms will be used to distinguish between different types of functions
and different use cases:
reducer: any function with the signature (state, action) -> newState (ie, any
function that could be used as an argument to Array.reduce )
root reducer: the reducer function that is actually passed as the first argument to
createStore . This is the only part of the reducer logic that must have the (state,
slice reducer: a reducer that is being used to handle updates to one specific slice
of the state tree, usually done by passing it to combineReducers
case function: a function that is being used to handle the update logic for a specific
action. This may actually be a reducer function, or it may require other parameters
to do its work properly.
higher-order reducer: a function that takes a reducer function as an argument,
and/or returns a new reducer function as a result (such as combineReducers , or
199
Splitting Reducer Logic
redux-undo )
The term "sub-reducer" has also been used in various discussions to mean any function
that is not the root reducer, although the term is not very precise. Some people may also
refer to some functions as "business logic" (functions that relate to application-specific
behavior) or "utility functions" (generic functions that are not application-specific).
Breaking down a complex process into smaller, more understandable parts is usually
described with the term functional decomposition. This term and concept can be
applied generically to any code. However, in Redux it is very common to structure
reducer logic using approach #3, where update logic is delegated to other functions
based on slice of state. Redux refers to this concept as reducer composition, and it is
by far the most widely-used approach to structuring reducer logic. In fact, it's so common
that Redux includes a utility function called combineReducers() , which specifically
abstracts the process of delegating work to other reducer functions based on slices of
state. However, it's important to note that it is not the only pattern that can be used. In
fact, it's entirely possible to use all three approaches for splitting up logic into functions,
and usually a good idea as well. The Refactoring Reducers section shows some
examples of this in action.
200
Refactoring Reducers Example
Initial Reducer
Let's say that our initial reducer looks like this:
201
Refactoring Reducers Example
const initialState = {
visibilityFilter : 'SHOW_ALL',
todos : []
};
202
Refactoring Reducers Example
That function is fairly short, but already becoming overly complex. We're dealing with two
different areas of concern (filtering vs managing our list of todos), the nesting is making
the update logic harder to read, and it's not exactly clear what's going on everywhere.
203
Refactoring Reducers Example
return updatedItems;
}
204
Refactoring Reducers Example
That reduced the duplication and made things a bit easier to read.
// Omitted
function updateObject(oldObject, newValues) {}
function updateItemInArray(array, itemId, updateItemCallback) {}
205
Refactoring Reducers Example
Now it's very clear what's happening in each case. We can also start to see some
patterns emerging.
// Omitted
function updateObject(oldObject, newValues) {}
function updateItemInArray(array, itemId, updateItemCallback) {}
return newTodos;
}
return newTodos;
}
return newTodos;
206
Refactoring Reducers Example
Notice that because the two "slice of state" reducers are now getting only their own part
of the whole state as arguments, they no longer need to return complex nested state
objects, and are now simpler as a result.
Reducing Boilerplate
We're almost done. Since many people don't like switch statements, it's very common to
use a function that creates a lookup table of action types to case functions. We'll use the
createReducer function described in Reducing Boilerplate:
207
Refactoring Reducers Example
// Omitted
function updateObject(oldObject, newValues) {}
function updateItemInArray(array, itemId, updateItemCallback) {}
// Omitted
function setVisibilityFilter(visibilityState, action) {}
// Omitted
function addTodo(todosState, action) {}
function toggleTodo(todosState, action) {}
function editTodo(todosState, action) {}
208
Refactoring Reducers Example
return updatedItems;
}
// Case reducer
function addTodo(todosState, action) {
const newTodos = todosState.concat({
id: action.id,
text: action.text,
completed: false
});
return newTodos;
}
// Case reducer
function toggleTodo(todosState, action) {
const newTodos = updateItemInArray(todosState, action.id, todo => {
return updateObject(todo, {completed : !todo.completed});
});
209
Refactoring Reducers Example
return newTodos;
}
// Case reducer
function editTodo(todosState, action) {
const newTodos = updateItemInArray(todosState, action.id, todo => {
return updateObject(todo, {text : action.text});
});
return newTodos;
}
// Slice reducer
const todosReducer = createReducer([], {
'ADD_TODO' : addTodo,
'TOGGLE_TODO' : toggleTodo,
'EDIT_TODO' : editTodo
});
// "Root reducer"
const appReducer = combineReducers({
visibilityFilter : visibilityReducer,
todos : todosReducer
});
We now have examples of several kinds of split-up reducer functions: helper utilities like
updateObject and createReducer , handlers for specific cases like setVisibilityFilter
Although the final result in this example is noticeably longer than the original version, this
is primarily due to the extraction of the utility functions, the addition of comments, and
some deliberate verbosity for the sake of clarity, such as separate return statements.
Looking at each function individually, the amount of responsibility is now smaller, and the
intent is hopefully clearer. Also, in a real application, these functions would probably then
be split into separate files such as reducerUtilities.js , visibilityReducer.js ,
todosReducer.js , and rootReducer.js .
210
Using combineReducers
Using combineReducers
Core Concepts
The most common state shape for a Redux app is a plain Javascript object containing
"slices" of domain-specific data at each top-level key. Similarly, the most common
approach to writing reducer logic for that state shape is to have "slice reducer" functions,
each with the same (state, action) signature, and each responsible for managing all
updates to that specific slice of state. Multiple slice reducers can respond to the same
action, independently update their own slice as needed, and the updated slices are
combined into the new state object.
One frequently asked question is whether Redux "calls all reducers" when
dispatching an action. Since there really is only one root reducer function, the
default answer is "no, it does not". However, combineReducers has specific behavior
that does work that way. In order to assemble the new state tree, combineReducers
will call each slice reducer with its current slice of state and the current action, giving
the slice reducer a chance to respond and update its slice of state if needed. So, in
that sense, using combineReducers does "call all reducers", or at least all of the slice
reducers it is wrapping.
You can use it at all levels of your reducer structure, not just to create the root
reducer. It's very common to have multiple combined reducers in various places,
211
Using combineReducers
combineReducers takes an object full of slice reducer functions, and creates a function
that outputs a corresponding state object with the same keys. This means that if no
preloaded state is provided to createStore , the naming of the keys in the input slice
reducer object will define the naming of the keys in the output state object. The
correlation between these names is not always apparent, especially when using ES6
features such as default module exports and object literal shorthands.
Here's an example of how use of ES6 object literal shorthand with combineReducers can
define the state shape:
212
Using combineReducers
// reducers.js
export default theDefaultReducer = (state = 0, action) => state;
// rootReducer.js
import {combineReducers, createStore} from "redux";
// Use ES6 object literal shorthand syntax to define the object shape
const rootReducer = combineReducers({
theDefaultReducer,
firstNamedReducer,
secondNamedReducer
});
Notice that because we used the ES6 shorthand for defining an object literal, the key
names in the resulting state are the same as the variable names from the imports. This
may not always be the desired behavior, and is often a cause of confusion for those who
aren't as familiar with ES6 syntax.
Also, the resulting names are a bit odd. It's generally not a good practice to actually
include words like "reducer" in your state key names - the keys should simply reflect the
domain or type of data they hold. This means we should either explicitly specify the
names of the keys in the slice reducer object to define the keys in the output state
object, or carefully rename the variables for the imported slice reducers to set up the
keys when using the shorthand object literal syntax.
213
Using combineReducers
// Rename the default import to whatever name we want. We can also rename a named
import.
import defaultState, {firstNamedReducer, secondNamedReducer as secondState} from "
./reducers";
This state shape better reflects the data involved, because we took care to set up the
keys we passed to combineReducers .
214
Beyond combineReducers
Beyond combineReducers
The combineReducers utility included with Redux is very useful, but is deliberately limited
to handle a single common use case: updating a state tree that is a plain Javascript
object, by delegating the work of updating each slice of state to a specific slice reducer.
It does not handle other use cases, such as a state tree made up of Immutable.js Maps,
trying to pass other portions of the state tree as an additional argument to a slice
reducer, or performing "ordering" of slice reducer calls. It also does not care how a given
slice reducer does its work.
The common question, then, is "How can I use combineReducers to handle these other
use cases?". The answer to that is simply: "you don't - you probably need to use
something else". Once you go past the core use case for combineReducers , it's time
to use more "custom" reducer logic, whether it be specific logic for a one-off use
case, or a reusable function that could be widely shared. Here's some suggestions for
dealing with a couple of these typical use cases, but feel free to come up with your own
approaches.
there are a number of published utilities that provide equivalent functionality, such as
redux-immutable. This package provides its own implementation of combineReducers
that knows how to iterate over an Immutable Map instead of a plain Javascript object.
215
Beyond combineReducers
Another alternative to the "shared-slice updates" issue would be to simply put more data
into the action. This is easily accomplished using thunk functions or a similar approach,
per this example:
function someSpecialActionCreator() {
return (dispatch, getState) => {
const state = getState();
const dataFromB = selectImportantDataFromB(state);
dispatch({
type : "SOME_SPECIAL_ACTION",
payload : {
dataFromB
}
});
}
}
Because the data from B's slice is already in the action, the parent reducer doesn't have
to do anything special to make that data available to sliceReducerA .
216
Beyond combineReducers
As it turns out, there's a useful utility called reduce-reducers that can make that process
easier. It simply takes multiple reducers and runs reduce() on them, passing the
intermediate state values to the next reducer in line:
Note that if you use reduceReducers , you should make sure that the first reducer in the
list is able to define the initial state, since the later reducers will generally assume that
the entire state already exists and not try to provide defaults.
Further Suggestions
217
Beyond combineReducers
Again, it's important to understand that Redux reducers are just functions. While
combineReducers is useful, it's just one tool in the toolbox. Functions can contain
conditional logic other than switch statements, functions can be composed to wrap each
other, and functions can call other functions. Maybe you need one of your slice reducers
to be able to reset its state, and to only respond to specific actions overall. You could do:
Note that combineReducers doesn't know or care that there's anything special about the
reducer function that's responsible for managing a . We didn't need to modify
combineReducers to specifically know how to undo things - we just built up the pieces we
Also, while combineReducers is the one reducer utility function that's built into Redux,
there's a wide variety of third-party reducer utilities that have published for reuse. The
Redux Addons Catalog lists many of the third-party utilities that are available. Or, if none
of the published utilities solve your use case, you can always write a function yourself
that does just exactly what you need.
218
Normalizing State Shape
const blogPosts = [
{
id : "post1",
author : {username : "user1", name : "User 1"},
body : "......",
comments : [
{
id : "comment1",
author : {username : "user2", name : "User 2"},
comment : ".....",
},
{
id : "comment2",
author : {username : "user3", name : "User 3"},
comment : ".....",
}
]
},
{
id : "post2",
author : {username : "user2", name : "User 2"},
body : "......",
comments : [
{
id : "comment3",
author : {username : "user3", name : "User 3"},
comment : ".....",
},
{
id : "comment4",
author : {username : "user1", name : "User 1"},
comment : ".....",
},
{
id : "comment5",
author : {username : "user3", name : "User 3"},
comment : ".....",
}
]
}
// and repeat many times
]
219
Normalizing State Shape
Notice that the structure of the data is a bit complex, and some of the data is repeated.
This is a concern for several reasons:
An example of a normalized state structure for the blog example above might look like:
{
posts : {
byId : {
"post1" : {
id : "post1",
author : "user1",
body : "......",
comments : ["comment1", "comment2"]
},
"post2" : {
id : "post2",
author : "user2",
body : "......",
comments : ["comment3", "comment4", "comment5"]
220
Normalizing State Shape
}
}
allIds : ["post1", "post2"]
},
comments : {
byId : {
"comment1" : {
id : "comment1",
author : "user2",
comment : ".....",
},
"comment2" : {
id : "comment2",
author : "user3",
comment : ".....",
},
"comment3" : {
id : "comment3",
author : "user3",
comment : ".....",
},
"comment4" : {
id : "comment4",
author : "user1",
comment : ".....",
},
"comment5" : {
id : "comment5",
author : "user3",
comment : ".....",
},
},
allIds : ["comment1", "comment2", "comment3", "commment4", "comment5"]
},
users : {
byId : {
"user1" : {
username : "user1",
name : "User 1",
}
"user2" : {
username : "user2",
name : "User 2",
}
"user3" : {
username : "user3",
name : "User 3",
}
},
allIds : ["user1", "user2", "user3"]
}
}
221
Normalizing State Shape
This state structure is much flatter overall. Compared to the original nested format, this is
an improvement in several ways:
Because each item is only defined in one place, we don't have to try to make
changes in multiple places if that item is updated.
The reducer logic doesn't have to deal with deep levels of nesting, so it will probably
be much simpler.
The logic for retrieving or updating a given item is now fairly simple and consistent.
Given an item's type and its ID, we can directly look it up in a couple simple steps,
without having to dig through other objects to find it.
Since each data type is separated, an update like changing the text of a comment
would only require new copies of the "comments > byId > comment" portion of the
tree. This will generally mean fewer portions of the UI that need to update because
their data has changed. In contrast, updating a comment in the original nested
shape would have required updating the the comment object, the parent post object,
and the array of all post objects, and likely have caused all of the Post components
and Comment components in the UI to re-render themselves.
Note that a normalized state structure generally implies that more components are
connected and each component is responsible for looking up its own data, as opposed
to a few connected components looking up large amounts of data and passing all that
data downwards. As it turns out, having connected parent components simply pass item
IDs to connected children is a good pattern for optimizing UI performance in a React
Redux application, so keeping state normalized plays a key role in improving
performance.
222
Normalizing State Shape
{
simpleDomainData1: {....},
simpleDomainData2: {....}
entities : {
entityType1 : {....},
entityType2 : {....}
}
ui : {
uiSection1 : {....},
uiSection2 : {....}
}
}
This could be expanded in a number of ways. For example, an application that does a lot
of editing of entities might want to keep two sets of "tables" in the state, one for the
"current" item values and one for the "work-in-progress" item values. When an item is
edited, its values could be copied into the "work-in-progress" section, and any actions
that update it would be applied to the "work-in-progress" copy, allowing the editing form
to be controlled by that set of data while another part of the UI still refers to the original
version. "Resetting" the edit form would simply require removing the item from the "work-
in-progress" section and re-copying the original data from "current" to "work-in-
progress", while "applying" the edits would involve copying the values from the "work-in-
progress" section to the "current" section.
223
Normalizing State Shape
{
entities: {
authors : { byId : {}, allIds : [] },
books : { byId : {}, allIds : [] },
authorBook : {
byId : {
1 : {
id : 1,
authorId : 5,
bookId : 22
},
2 : {
id : 2,
authorId : 5,
bookId : 15,
}
3 : {
id : 3,
authorId : 42,
bookId : 12
}
},
allIds : [1, 2, 3]
}
}
}
Operations like "Look up all books by this author" can then accomplished with a single
loop over the join table. Given the typical amounts of data in a client application and the
speed of Javascript engines, this kind of operation is likely to have sufficiently fast
performance for most use cases.
224
Normalizing State Shape
225
Updating Normalized Data
Standard Approaches
Simple Merging
One approach is to merge the contents of the action in to the existing state. In this case,
we need to do a deep recursive merge, not just a shallow copy. The Lodash merge
function can handle this for us:
This requires the least amount of work on the reducer side, but does require that the
action creator potentially do a fair amount of work to organize the data into the correct
shape before the action is dispatched. It also doesn't handle trying to delete an item.
226
Updating Normalized Data
Comment object using that ID as a key, and include the Comment's ID in the list of all
Comment IDs. Here's how the pieces for this might fit together:
// actions.js
function addComment(postId, commentText) {
// Generate a unique ID for this comment
const commentId = generateId("comment");
return {
type : "ADD_COMMENT",
payload : {
postId,
commentId,
commentText
}
};
}
// reducers/posts.js
function addComment(state, action) {
const {payload} = action;
const {postId, commentId} = payload;
return {
...state,
// Update our Post object with a new "comments" array
[postId] : {
...post,
comments : post.comments.concat(commentId)
}
};
}
227
Updating Normalized Data
// reducers/comments.js
function addCommentEntry(state, action) {
const {payload} = action;
const {commentId, commentText} = payload;
// Insert the new Comment object into the updated lookup table
return {
...state,
[commentId] : comment
};
}
The example is a bit long, because it's showing how all the different slice reducers and
case reducers fit together. Note that the delegation involved here. The postsById slice
reducer delegates the work for this case to addComment , which inserts the new
Comment's ID into the correct Post item. Meanwhile, both the commentsById and
allComments slice reducers have their own case reducers, which update the Comments
Other Approaches
228
Updating Normalized Data
Task-Based Updates
Since reducers are just functions, there's an infinite number of ways to split up this logic.
While using slice reducers is obviously the most common, it's also possible to organize
behavior in a more task-oriented structure. Because this will often involve more nested
updates, you may want to use an immutable update utility library like dot-prop-immutable
or object-path-immutable to simplify the update statements. Here's an example of what
that might look like:
229
Updating Normalized Data
return updatedWithCommentsList;
}
This approach makes it very clear what's happening for the "ADD_COMMENTS" case, but it
does require nested updating logic, and some specific knowledge of the state tree
shape. Depending on how you want to compose your reducer logic, this may or may not
be desired.
230
Updating Normalized Data
Redux-ORM
The Redux-ORM library provides a very useful abstraction layer for managing
normalized data in a Redux store. It allows you to declare Model classes and define
relations between them. It can then generate the empty "tables" for your data types, act
as a specialized selector tool for looking up the data, and perform immutable updates on
that data.
There's a couple ways Redux-ORM can be used to perform updates. First, the Redux-
ORM docs suggest defining reducer functions on each Model subclass, then including
the auto-generated combined reducer function into your store:
// models.js
import {Model, many, Schema} from "redux-orm";
231
Updating Normalized Data
// Create a Schema instance, and hook up the Post and Comment models
export const schema = new Schema();
schema.register(Post, Comment);
// main.js
import { createStore, combineReducers } from 'redux'
import {schema} from "./models";
232
Updating Normalized Data
// Assume this case reducer is being used in our "entities" slice reducer,
// and we do not have reducers defined on our Redux-ORM Model subclasses
function addComment(entitiesState, action) {
const session = schema.from(entitiesState);
const {Post, Comment} = session;
const {payload} = action;
const {postId, commentId, commentText} = payload;
return session.reduce();
}
Overall, Redux-ORM provides a very useful set of abstractions for defining relations
between data types, creating the "tables" in our state, retrieving and denormalizing
relational data, and applying immutable updates to relational data.
233
Reusing Reducer Logic
As an example, let's say that we want to track multiple counters in our application,
named A, B, and C. We define our initial counter reducer, and we use
combineReducers to set up our state:
Unfortunately, this setup has a problem. Because combineReducers will call each slice
reducer with the same action, dispatching {type : 'INCREMENT'} will actually cause all
three counter values to be incremented, not just one of them. We need some way to
wrap the counter logic so that we can ensure that only the counter we care about is
updated.
234
Reusing Reducer Logic
The two most common ways to specialize a reducer are to generate new action
constants with a given prefix or suffix, or to attach additional info inside the action object.
Here's what those might look like:
switch (action.type) {
case `INCREMENT`:
return state + 1;
case `DECREMENT`:
return state - 1;
default:
return state;
}
}
}
We should now be able to use either of these to generate our specialized counter
reducers, and then dispatch actions that will affect the portion of the state that we care
about:
235
Reusing Reducer Logic
store.dispatch({type : 'INCREMENT_B'});
console.log(store.getState());
// {counterA : 0, counterB : 1, counterC : 0}
We could also vary the approach somewhat, and create a more generic higher-order
reducer that accepts both a given reducer function and a name or identifier:
236
Reusing Reducer Logic
These basic patterns allow you to do things like having multiple instances of a smart
connected component within the UI, or reuse common logic for generic capabilities such
as pagination or sorting.
237
Immutable Update Patterns
Common Mistake #1: New variables that point to the same objects
Defining a new variable does not create a new actual object - it only creates another
reference to the same object. An example of this error would be:
nestedState.nestedField = action.data;
return {
...state,
nestedState
};
}
This function does correctly return a shallow copy of the top-level state object, but
because the nestedState variable was still pointing at the existing object, the state was
directly mutated.
238
Immutable Update Patterns
return newState;
}
Doing a shallow copy of the top level is not sufficient - the nestedState object should be
copied as well.
Obviously, each layer of nesting makes this harder to read, and gives more chances to
make mistakes. This is one of several reasons why you are encouraged to keep your
state flattened, and compose reducers as much as possible.
239
Immutable Update Patterns
Normally, a Javascript array's contents are modified using mutative functions like push ,
unshift , and splice . Since we don't want to mutate state directly in reducers, those
should normally be avoided. Because of that, you might see "insert" or "remove"
behavior written like this:
However, remember that the key is that the original in-memory reference is not modified.
As long as we make a copy first, we can safely mutate the copy. Note that this is
true for both arrays and objects, but nested values still must be updated using the same
rules.
This means that we could also write the insert and remove functions like this:
240
Immutable Update Patterns
They can provide a useful alternative to writing manual immutable update logic.
A list of many immutable update utilities can be found in the Immutable Data#Immutable
Update Utilities section of the Redux Addons Catalog.
241
Immutable Update Patterns
242
Initializing State
Initializing State
There are two main ways to initialize state for your application. The createStore
method can accept an optional preloadedState value as its second argument. Reducers
can also specify an initial value by looking for an incoming state argument that is
undefined , and returning the value they'd like to use as a default. This can either be
done with an explicit check inside the reducer, or by using the ES6 default argument
value syntax: function myReducer(state = someDefaultValue, action) .
It's not always immediately clear how these two approaches interact. Fortunately, the
process does follow some predictable rules. Here's how the pieces fit together.
Summary
Without combineReducers() or similar manual code, preloadedState always wins over
state = ... in the reducer because the state passed to the reducer is
preloadedState and is not undefined , so the ES6 argument syntax doesn't apply.
With combineReducers() the behavior is more nuanced. Those reducers whose state is
specified in preloadedState will receive that state. Other reducers will receive
undefined and because of that will fall back to the state = ... default argument they
specify.
In general, preloadedState wins over the state specified by the reducer. This lets
reducers specify initial data that makes sense to them as default arguments, but
also allows loading existing data (fully or partially) when you're hydrating the
store from some persistent storage or the server.
In Depth
Single Simple Reducer
First let's consider a case where you have a single reducer. Say you don't use
combineReducers() .
243
Initializing State
The initial state is zero. Why? Because the second argument to createStore was
undefined . This is the state passed to your reducer the first time. When Redux
initializes it dispatches a "dummy" action to fill the state. So your counter reducer was
called with state equal to undefined . This is exactly the case that "activates" the
default argument. Therefore, state is now 0 as per the default state value ( state
= 0 ). This state ( 0 ) will be returned.
Why is it 42 , and not 0 , this time? Because createStore was called with 42 as the
second argument. This argument becomes the state passed to your reducer along
with the dummy action. This time, state is not undefined (it's 42 !), so ES6 default
argument syntax has no effect. The state is 42 , and 42 is returned from the
reducer.
Combined Reducers
Now let's consider a case where you use combineReducers() .
244
Initializing State
If we call createStore without the preloadedState , it's going to initialize the state to
{} . Therefore, state.a and state.b will be undefined by the time it calls a and b
245
Initializing State
In this case, state was specified so it didn't fall back to {} . It was an object with a
field equal to 'horse' , but without the b field. This is why the a reducer received
'horse' as its state and gladly returned it, but the b reducer received undefined as
its state and thus returned its idea of the default state (in our example, 'wat' ). This
is how we get { a: 'horse', b: 'wat' } in return.
Recap
To sum this up, if you stick to Redux conventions and return the initial state from
reducers when they're called with undefined as the state argument (the easiest way
to implement this is to specify the state ES6 default argument value), you're going to
have a nice useful behavior for combined reducers. They will prefer the
corresponding value in the preloadedState object you pass to the createStore()
function, but if you didn't pass any, or if the corresponding field is not set, the
default state argument specified by the reducer is chosen instead. This approach
works well because it provides both initialization and hydration of existing data, but lets
individual reducers reset their state if their data was not preserved. Of course you can
apply this pattern recursively, as you can use combineReducers() on many levels, or
even compose reducers manually by calling reducers and giving them the relevant part
of the state tree.
246
FAQ
Redux FAQ
Table of Contents
General
Do I have to put all my state into Redux? Should I ever use React's setState()?
Can I put functions, promises, or other non-serializable items in my store state?
How do I organize nested or duplicate data in my state?
Store Setup
Can or should I create multiple stores? Can I import my store directly, and use
it in components myself?
Is it OK to have more than one middleware chain in my store enhancer? What
is the difference between next and dispatch in a middleware function?
How do I subscribe to only a portion of the state? Can I get the dispatched
action as part of the subscription?
Actions
What should my file structure look like? How should I group my action creators
247
FAQ
248
General
Table of Contents
When should I use Redux?
Can Redux only be used with React?
Do I need to have a particular build tool to use Redux?
General
When should I use Redux?
Pete Hunt, one of the early contributors to React, says:
You'll know when you need Flux. If you aren't sure if you need it, you don't need it.
I would like to amend this: don't use Redux until you have problems with vanilla
React.
In general, use Redux when you have reasonable amounts of data changing over time,
you need a single source of truth, and you find that approaches like keeping everything
in a top-level React component's state are no longer sufficient.
However, it's also important to understand that using Redux comes with tradeoffs. It's
not designed to be the shortest or fastest way to write code. It's intended to help answer
the question "When did a certain slice of state change, and where did the data come
from?", with predictable behavior. It does so by asking you to follow specific constraints
in your application: store your application's state as plain data, describe changes as
plain objects, and handle those changes with pure functions that apply updates
immutably. This is often the source of complaints about "boilerplate". These constraints
require effort on the part of a developer, but also open up a number of additional
possibilities (such as store persistence and synchronization).
If you're just learning React, you should probably focus on thinking in React first, then
look at Redux once you better understand React and how Redux might fit into your
application.
249
General
In the end, Redux is just a tool. It's a great tool, and there's some great reasons to use it,
but there's also reasons you might not want to use it. Make informed decisions about
your tools, and understand the tradeoffs involved in each decision.
Further information
Documentation
Introduction: Motivation
Articles
React How-To
You Might Not Need Redux
The Case for Flux
Discussions
250
General
Redux is originally written in ES6 and transpiled for production into ES5 with Webpack
and Babel. You should be able to use it regardless of your JavaScript build process.
Redux also offers a UMD build that can be used directly without any build process at all.
The counter-vanilla example demonstrates basic ES5 usage with Redux included as a
<script> tag. As the relevant pull request says:
The new Counter Vanilla example is aimed to dispel the myth that Redux requires
Webpack, React, hot reloading, sagas, action creators, constants, Babel, npm,
CSS modules, decorators, fluent Latin, an Egghead subscription, a PhD, or an
Exceeds Expectations O.W.L. level.
Nope, it's just HTML, some artisanal <script> tags, and plain old DOM
manipulation. Enjoy!
251
Reducers
Table of Contents
How do I share state between two reducers? Do I have to use combineReducers?
Do I have to use the switch statement to handle actions?
Reducers
How do I share state between two reducers? Do I have to
use combineReducers ?
The suggested structure for a Redux store is to split the state object into multiple slices
or domains by key, and provide a separate reducer function to manage each individual
data slice. This is similar to how the standard Flux pattern has multiple independent
stores, and Redux provides the combineReducers utility function to make this pattern
easier. However, it's important to note that combineReducers is not requiredit is simply
a utility function for the common use case of having a single reducer function per state
slice, with plain JavaScript objects for the data.
Many users later want to try to share data between two reducers, but find that
combineReducers does not allow them to do so. There are several approaches that can
be used:
If a reducer needs to know data from another slice of state, the state tree shape
may need to be reorganized so that a single reducer is handling more of the data.
You may need to write some custom functions for handling some of these actions.
This may require replacing combineReducers with your own top-level reducer
function. You can also use a utility such as reduce-reducers to run combineReducers
to handle most actions, but also run a more specialized reducer for specific actions
that cross state slices.
Async action creators such as redux-thunk have access to the entire state through
getState() . An action creator can retrieve additional data from the state and put it
in an action, so that each reducer has enough information to update its own state
slice.
252
Reducers
In general, remember that reducers are just functionsyou can organize them and
subdivide them any way you want, and you are encouraged to break them down into
smaller, reusable functions (reducer composition). While you do so, you may pass a
custom third argument from a parent reducer if a child reducer needs additional data to
calculate its next state. You just need to make sure that together they follow the basic
rules of reducers: (state, action) => newState , and update state immutably rather than
mutating it directly.
Further information
Documentation
API: combineReducers
Recipes: Structuring Reducers
Discussions
Further information
Documentation
253
Reducers
Discussions
254
Organizing State
Table of Contents
Do I have to put all my state into Redux? Should I ever use React's setState()?
Can I put functions, promises, or other non-serializable items in my store state?
How do I organize nested or duplicate data in my state?
Organizing State
Do I have to put all my state into Redux? Should I ever
use React's setState() ?
There is no right answer for this. Some users prefer to keep every single piece of data
in Redux, to maintain a fully serializable and controlled version of their application at all
times. Others prefer to keep non-critical or UI state, such as is this dropdown currently
open, inside a component's internal state.
Using local component state is fine. As a developer, it is your job to determine what
kinds of state make up your application, and where each piece of state should live. Find
a balance that works for you, and go with it.
Some common rules of thumb for determing what kind of data should be put into Redux:
There are a number of community packages that implement various approaches for
storing per-component state in a Redux store instead, such as redux-ui, redux-
component, redux-react-local, and more. It's also possible to apply Redux's principles
and concept of reducers to the task of updating local component state as well, along the
lines of this.setState( (previousState) => reducer(previousState, someAction)) .
255
Organizing State
Further information
Articles
Discussions
Libraries
If you are okay with things like persistence and time-travel debugging potentially not
working as intended, then you are totally welcome to put non-serializable items into your
Redux store. Ultimately, it's your application, and how you implement it is up to you. As
256
Organizing State
with many other things about Redux, just be sure you understand what tradeoffs are
involved.
Further information
Discussions
Further information
Documentation
Articles
High-Performance Redux
Querying a Redux Store
Discussions
257
Organizing State
#946: Best way to update related state fields with split reducers?
#994: How to cut the boilerplate when updating nested entities?
#1255: Normalizr usage with nested objects in React/Redux
#1269: Add tree view example
#1824: Normalising state and garbage collection
Twitter: state shape should be normalized
Stack Overflow: How to handle tree-shaped entities in Redux reducers?
Stack Overflow: How to optimize small updates to props of nested components in
React + Redux?
258
Store Setup
Table of Contents
Can or should I create multiple stores? Can I import my store directly, and use it in
components myself?
Is it OK to have more than one middleware chain in my store enhancer? What is the
difference between next and dispatch in a middleware function?
How do I subscribe to only a portion of the state? Can I get the dispatched action as
part of the subscription?
Store Setup
Can or should I create multiple stores? Can I import my
store directly, and use it in components myself?
The original Flux pattern describes having multiple stores in an app, each one holding
a different area of domain data. This can introduce issues such as needing to have one
store waitFor another store to update. This is not necessary in Redux because the
separation between data domains is already achieved by splitting a single reducer into
smaller reducers.
As with several other questions, it is possible to create multiple distinct Redux stores in a
page, but the intended pattern is to have only a single store. Having a single store
enables using the Redux DevTools, makes persisting and rehydrating data simpler, and
simplifies the subscription logic.
Some valid reasons for using multiple stores in Redux might include:
Solving a performance issue caused by too frequent updates of some part of the
state, when confirmed by profiling the app.
Isolating a Redux app as a component in a bigger application, in which case you
might want to create a store per root component instance.
However, creating new stores shouldn't be your first instinct, especially if you come from
a Flux background. Try reducer composition first, and only use multiple stores if it
doesn't solve your problem.
259
Store Setup
Similarly, while you can reference your store instance by importing it directly, this is not a
recommended pattern in Redux. If you create a store instance and export it from a
module, it will become a singleton. This means it will be harder to isolate a Redux app as
a component of a larger app, if this is ever necessary, or to enable server rendering,
because on the server you want to create separate store instances for every request.
With React Redux, the wrapper classes generated by the connect() function do
actually look for props.store if it exists, but it's best if you wrap your root component in
<Provider store={store}> and let React Redux worry about passing the store down.
This way components don't need to worry about importing a store module, and isolating
a Redux app or enabling server rendering is much easier to do later.
Further information
Documentation
API: Store
Discussions
Further information
260
Store Setup
Documentation
Advanced: Middleware
API: applyMiddleware
Discussions
The new state is not passed to the listeners in order to simplify implementing store
enhancers such as the Redux DevTools. In addition, subscribers are intended to react to
the state value itself, not the action. Middleware can be used if the action is important
and needs to be handled specifically.
Further information
Documentation
Basics: Store
API: Store
Discussions
261
Store Setup
Libraries
262
Actions
Table of Contents
Why should type be a string, or at least serializable? Why should my action types be
constants?
Is there always a one-to-one mapping between reducers and actions?
How can I represent side effects such as AJAX calls? Why do we need things like
action creators, thunks, and middleware to do async behavior?
Should I dispatch multiple actions in a row from one action creator?
Actions
Why should type be a string, or at least serializable?
Why should my action types be constants?
As with state, serializable actions enable several of Redux's defining features, such as
time travel debugging, and recording and replaying actions. Using something like a
Symbol for the type value or using instanceof checks for actions themselves would
break that. Strings are serializable and easily self-descriptive, and so are a better choice.
Note that it is okay to use Symbols, Promises, or other non-serializable values in an
action if the action is intended for use by middleware. Actions only need to be
serializable by the time they actually reach the store and are passed to the reducers.
We can't reliably enforce serializable actions for performance reasons, so Redux only
checks that every action is a plain object, and that the type is defined. The rest is up to
you, but you might find that keeping everything serializable helps debug and reproduce
issues.
263
Actions
Further information
Documentation
Reducing Boilerplate
Discussion
Further information
Documentation
Basics: Reducers
Recipes: Structuring Reducers
Discussions
264
Actions
Any meaningful web app needs to execute complex logic, usually including
asynchronous work such as making AJAX requests. That code is no longer purely a
function of its inputs, and the interactions with the outside world are known as side
effects
Redux is inspired by functional programming, and out of the box, has no place for side
effects to be executed. In particular, reducer functions must always be pure functions of
(state, action) => newState . However, Redux's middleware makes it possible to
intercept dispatched actions and add additional complex behavior around them,
including side effects.
In general, Redux suggests that code with side effects should be part of the action
creation process. While that logic can be performed inside of a UI component, it
generally makes sense to extract that logic into a reusable function so that the same
logic can be called from multiple placesin other words, an action creator function.
The simplest and most common way to do this is to add the Redux Thunk middleware
that lets you write action creators with more complex and asynchronous logic. Another
widely-used method is Redux Saga which lets you write more synchronous-looking code
using generators, and can act like background threads or daemons in a Redux app.
Yet another approach is Redux Loop, which inverts the process by allowing your
reducers to declare side effects in response to state changes and have them executed
separately. Beyond that, there are many other community-developed libraries and ideas,
each with their own take on how side effects should be managed.
Further information
Documentation
Articles
265
Actions
Discussions
In general, ask if these actions are related but independent, or should actually be
represented as one action. Do what makes sense for your own situation but try to
balance the readability of reducers with readability of the action log. For example, an
action that includes the whole new state tree would make your reducer a one-liner, but
the downside is now you have no history of why the changes are happening, so
266
Actions
debugging gets really difficult. On the other hand, if you emit actions in a loop to keep
them granular, it's a sign that you might want to introduce a new action type that is
handled in a different way.
Try to avoid dispatching several times synchronously in a row in the places where you're
concerned about performance. There are a number of addons and approaches that can
batch up dispatches as well.
Further information
Documentation
Discussions
267
Code Structure
Table of Contents
What should my file structure look like? How should I group my action creators and
reducers in my project? Where should my selectors go?
How should I split my logic between reducers and action creators? Where should
my business logic go?
Code Structure
What should my file structure look like? How should I
group my action creators and reducers in my project?
Where should my selectors go?
Since Redux is just a data store library, it has no direct opinion on how your project
should be structured. However, there are a few common patterns that most Redux
developers tend to use:
It's generally suggested that selectors are defined alongside reducers and exported, and
then reused elsewhere (such as in mapStateToProps functions, in async action creators,
or sagas) to colocate all the code that knows about the actual shape of the state tree in
the reducer files.
While it ultimately doesn't matter how you lay out your code on disk, it's important to
remember that actions and reducers shouldn't be considered in isolation. It's entirely
possible (and encouraged) for a reducer defined in one folder to respond to an action
defined in another folder.
268
Code Structure
Further information
Documentation
Articles
Discussions
269
Code Structure
Now, the problem is what to put in the action creator and what in the reducer, the
choice between fat and thin action objects. If you put all the logic in the action
creator, you end up with fat action objects that basically declare the updates to the
state. Reducers become pure, dumb, add-this, remove that, update these
functions. They will be easy to compose. But not much of your business logic will
be there. If you put more logic in the reducer, you end up with nice, thin action
objects, most of your data logic in one place, but your reducers are harder to
compose since you might need info from other branches. You end up with large
reducers or reducers that take additional arguments from higher up in the state.
Find the balance between these two extremes, and you will master Redux.
Further information
Articles
Discussions
270
Performance
Table of Contents
How well does Redux scale in terms of performance and architecture?
Won't calling all my reducers for each action be slow?
Do I have to deep-clone my state in a reducer? Isn't copying my state going to be
slow?
How can I reduce the number of store update events?
Will having one state tree cause memory problems? Will dispatching many actions
take up memory?
Performance
How well does Redux scale in terms of performance
and architecture?
While there's no single definitive answer to this, most of the time this should not be a
concern in either case.
The work done by Redux generally falls into a few areas: processing actions in
middleware and reducers (including object duplication for immutable updates), notifying
subscribers after actions are dispatched, and updating UI components based on the
state changes. While it's certainly possible for each of these to become a performance
concern in sufficiently complex situations, there's nothing inherently slow or inefficient
about how Redux is implemented. In fact, React Redux in particular is heavily optimized
to cut down on unnecessary re-renders, and React-Redux v5 shows noticeable
improvements over earlier versions.
Redux may not be as efficient out of the box when compared to other libraries. For
maximum rendering performance in a React application, state should be stored in a
normalized shape, many individual components should be connected to the store
instead of just a few, and connected list components should pass item IDs to their
connected child list items (allowing the list items to look up their own data by ID). This
minimizes the overall amount of rendering to be done. Use of memoized selector
functions is also an important performance consideration.
271
Performance
As for architecture, anecdotal evidence is that Redux works well for varying project and
team sizes. Redux is currently used by hundreds of companies and thousands of
developers, with several hundred thousand monthly installations from NPM. One
developer reported:
for scale, we have ~500 action types, ~400 reducer cases, ~150 components, 5
middlewares, ~200 actions, ~2300 tests
Further information
Documentation
Articles
Discussions
272
Performance
It's important to note that a Redux store really only has a single reducer function. The
store passes the current state and dispatched action to that one reducer function, and
lets the reducer handle things appropriately.
Obviously, trying to handle every possible action in a single function does not scale well,
simply in terms of function size and readability, so it makes sense to split the actual work
into separate functions that can be called by the top-level reducer. In particular, the
common suggested pattern is to have a separate sub-reducer function that is
responsible for managing updates to a particular slice of state at a specific key. The
combineReducers() that comes with Redux is one of the many possible ways to achieve
this. It's also highly suggested to keep your store state as flat and as normalized as
possible. Ultimately, though, you are in charge of organizing your reducer logic any way
you want.
However, even if you happen to have many different reducer functions composed
together, and even with deeply nested state, reducer speed is unlikely to be a problem.
JavaScript engines are capable of running a very large number of function calls per
second, and most of your reducers are probably just using a switch statement and
returning the existing state by default in response to most actions.
If you actually are concerned about reducer performance, you can use a utility such as
redux-ignore or reduxr-scoped-reducer to ensure that only certain reducers listen to
specific actions. You can also use redux-log-slow-reducers to do some performance
benchmarking.
Further information
Discussions
273
Performance
However, you do need to create a copied and updated object for each level of nesting
that is affected. Although that shouldn't be particularly expensive, it's another good
reason why you should keep your state normalized and shallow if possible.
Common Redux misconception: you need to deeply clone the state. Reality: if
something inside doesn't change, keep its reference the same!
Further information
Documentation
Discussions
If you use React, note that you can improve performance of multiple synchronous
dispatches by wrapping them in ReactDOM.unstable_batchedUpdates() , but this API is
experimental and may be removed in any React release so don't rely on it too heavily.
Take a look at redux-batched-actions (a higher-order reducer that lets you dispatch
several actions as if it was one and unpack them in the reducer), redux-batched-
subscribe (a store enhancer that lets you debounce subscriber calls for multiple
dispatches), or redux-batch (a store enhancer that handles dispatching an array of
actions with a single subscriber notification).
Further information
Discussions
274
Performance
Libraries
Redux does not store a history of actions itself. However, the Redux DevTools do store
actions so they can be replayed, but those are generally only enabled during
development, and not used in production.
Further information
Documentation
Discussions
Stack Overflow: Is there any way to "commit" the state in Redux to free memory?
Reddit: What's the best place to keep initial state?
275
React Redux
Table of Contents
Why isn't my component re-rendering, or my mapStateToProps running?
Why is my component re-rendering too often?
How can I speed up my mapStateToProps?
Why don't I have this.props.dispatch available in my connected component?
Should I only connect my top component, or can I connect multiple components in
my tree?
React Redux
Why isn't my component re-rendering, or my
mapStateToProps running?
Accidentally mutating or modifying your state directly is by far the most common reason
why components do not re-render after an action has been dispatched. Redux expects
that your reducers will update their state immutably, which effectively means always
making copies of your data, and applying your changes to the copies. If you return the
same object from a reducer, Redux assumes that nothing has been changed, even if
you made changes to its contents. Similarly, React Redux tries to improve performance
by doing shallow equality reference checks on incoming props in
shouldComponentUpdate , and if all references are the same, returns false to skip
It's important to remember that whenever you update a nested value, you must also
return new copies of anything above it in your state tree. If you have state.a.b.c.d , and
you want to make an update to d , you would also need to return new copies of c , b ,
a , and state . This state tree mutation diagram demonstrates how a change deep in a
Note that updating data immutably does not mean that you must use Immutable.js,
although that is certainly an option. You can do immutable updates to plain JS objects
and arrays using several different approaches:
276
React Redux
Further information
Documentation
Troubleshooting
React Redux: Troubleshooting
Recipes: Using the Object Spread Operator
Recipes: Structuring Reducers - Prerequisite Concepts
Recipes: Structuring Reducers - Immutable Update Patterns
Articles
Discussions
277
React Redux
Even though the array might contain the exact same object references each time, the
array itself is a different reference, so the shallow equality check fails and React Redux
would re-render the wrapped component.
The extra re-renders could be resolved by saving the array of objects into the state using
a reducer, caching the mapped array using Reselect, or implementing
shouldComponentUpdate in the component by hand and doing a more in-depth props
comparison using a function such as _.isEqual . Be careful to not make your custom
shouldComponentUpdate() more expensive than the rendering itself! Always use a profiler
For non-connected components, you may want to check what props are being passed
in. A common issue is having a parent component re-bind a callback inside its render
function, like <Child onClick={this.handleClick.bind(this)} /> . That creates a new
function reference every time the parent re-renders. It's generally good practice to only
bind callbacks once in the parent component's constructor.
Further information
Documentation
Articles
Discussions
Libraries
278
React Redux
mapStateToProps runs quickly and also minimizes the amount of work it does. The
Further information
Documentation
Articles
Discussions
and pass those values as props to your component. The second, mapDispatchToProps , is
a function you provide to make use of the store's dispatch function, usually by creating
pre-bound versions of action creators that will automatically dispatch their actions as
soon as they are called.
If you do not provide your own mapDispatchToProps function when calling connect() ,
React Redux will provide a default version, which simply returns the dispatch function
as a prop. That means that if you do provide your own function, dispatch is not
279
React Redux
automatically provided. If you still want it available as a prop, you need to explicitly return
it yourself in your mapDispatchToProps implementation.
Further information
Documentation
Discussions
React Redux #89: can i wrap multi actionCreators into one props with name?
React Redux #145: consider always passing down dispatch regardless of what
mapDispatchToProps does
React Redux #255: this.props.dispatch is undefined if using mapDispatchToProps
Stack Overflow: How to get simple dispatch from this.props using connect w/
Redux?
In fact, benchmarks have shown that more connected components generally leads to
better performance than fewer connected components.
280
React Redux
In general, try to find a balance between understandable data flow and areas of
responsibility with your components.
Further information
Documentation
Articles
Discussions
281
Miscellaneous
Table of Contents
Are there any larger, real Redux projects?
How can I implement authentication in Redux?
Miscellaneous
Are there any larger, real Redux projects?
Yes, lots of them! To name just a few:
And many, many more! The Redux Addons Catalog has a list of Redux-based
applications and examples that points to a variety of actual applications, large and
small.
Further information
Documentation
Introduction: Examples
Discussions
282
Miscellaneous
Authentication is essential to any real application. When going about authentication you
must keep in mind that nothing changes with how you should organize your application
and you should implement authentication in the same way you would any other feature.
It is relatively straightforward:
2. Create action creators that take in credentials, a flag that signifies whether
authentication succeeded, a token, or an error message as the payload.
3. Create an async action creator with Redux Thunk middleware or any middleware
you see fit to fire a network request to an API that returns a token if the credentials
are valid. Then save the token in the local storage or show a response to the user if
it failed. You can perform these side effects from the action creators you wrote in
the previous step.
4. Create a reducer that returns the next state for each possible authentication case
( LOGIN_SUCCESS , LOGIN_FAILURE , etc).
Further information
Articles
Examples
react-redux-jwt-auth-example
Libraries
283
Troubleshooting
Troubleshooting
This is a place to share common problems and solutions to them.
The examples use React, but you should still find them useful if you use something else.
Redux assumes that you never mutate the objects it gives to you in the reducer. Every
single time, you must return the new state object. Even if you don't use a library like
Immutable, you need to completely avoid mutation.
For example, a reducer like this is wrong because it mutates the state:
284
Troubleshooting
It's more code, but it's exactly what makes Redux predictable and efficient. If you want to
have less code, you can use a helper like React.addons.update to write immutable
transformations with a terse syntax:
// Before:
return state.map((todo, index) => {
if (index === action.index) {
return Object.assign({}, todo, {
completed: true
})
}
return todo
})
// After
return update(state, {
[action.index]: {
completed: {
$set: true
}
}
})
285
Troubleshooting
Finally, to update objects, you'll need something like _.extend from Underscore, or
better, an Object.assign polyfill.
Make sure that you use Object.assign correctly. For example, instead of returning
something like Object.assign(state, newData) from your reducers, return
Object.assign({}, state, newData) . This way you don't override the previous state .
You can also enable the object spread operator proposal for a more succinct syntax:
// Before:
return state.map((todo, index) => {
if (index === action.index) {
return Object.assign({}, todo, {
completed: true
})
}
return todo
})
// After:
return state.map((todo, index) => {
if (index === action.index) {
return { ...todo, completed: true }
}
return todo
})
Also keep an eye out for nested state objects that need to be deeply copied. Both
_.extend and Object.assign make a shallow copy of the state. See Updating Nested
TodoActions.js
AddTodo.js
286
Troubleshooting
render() {
return (
<button onClick={() => this.handleClick()}>
Add
</button>
)
}
}
It doesn't work because your action creator is just a function that returns an action. It is
up to you to actually dispatch it. We can't bind your action creators to a particular Store
instance during the definition because apps that render on the server need a separate
Redux store for every request.
handleClick() {
// Works! (but you need to grab store somehow)
store.dispatch(addTodo('Fix the issue'))
}
AddTodo.js
287
Troubleshooting
render() {
return (
<button onClick={() => this.handleClick()}>
Add
</button>
)
}
}
You can then pass dispatch down to other components manually, if you want to.
If you figure it out, edit this document as a courtesy to the next person having the same
problem.
288
Glossary
Glossary
This is a glossary of the core terms in Redux, along with their type signatures. The types
are documented using Flow notation.
State
type State = any
State (also called the state tree) is a broad term, but in the Redux API it usually refers to
the single state value that is managed by the store and returned by getState() . It
represents the entire state of a Redux application, which is often a deeply nested object.
By convention, the top-level state is an object or some other key-value collection like a
Map, but technically it can be any type. Still, you should do your best to keep the state
serializable. Don't put anything inside it that you can't easily turn into JSON.
Action
type Action = Object
An action is a plain object that represents an intention to change the state. Actions are
the only way to get data into the store. Any data, whether from UI events, network
callbacks, or other sources such as WebSockets needs to eventually be dispatched as
actions.
Actions must have a type field that indicates the type of action being performed. Types
can be defined as constants and imported from another module. It's better to use strings
for type than Symbols because strings are serializable.
Other than type , the structure of an action object is really up to you. If you're
interested, check out Flux Standard Action for recommendations on how actions should
be constructed.
289
Glossary
Reducer
type Reducer<S, A> = (state: S, action: A) => S
In Redux, the accumulated value is the state object, and the values being accumulated
are actions. Reducers calculate a new state given the previous state and an action. They
must be pure functionsfunctions that return the exact same output for given inputs.
They should also be free of side-effects. This is what enables exciting features like hot
reloading and time travel.
Dispatching Function
type BaseDispatch = (a: Action) => Action
type Dispatch = (a: Action | AsyncAction) => any
A dispatching function (or simply dispatch function) is a function that accepts an action
or an async action; it then may or may not dispatch one or more actions to the store.
We must distinguish between dispatching functions in general and the base dispatch
function provided by the store instance without any middleware.
The base dispatch function always synchronously sends an action to the store's reducer,
along with the previous state returned by the store, to calculate a new state. It expects
actions to be plain objects ready to be consumed by the reducer.
Middleware wraps the base dispatch function. It allows the dispatch function to handle
async actions in addition to actions. Middleware may transform, delay, ignore, or
otherwise interpret actions or async actions before passing them to the next middleware.
290
Glossary
Action Creator
type ActionCreator = (...args: any) => Action | AsyncAction
An action creator is, quite simply, a function that creates an action. Do not confuse the
two termsagain, an action is a payload of information, and an action creator is a
factory that creates an action.
Calling an action creator only produces an action, but does not dispatch it. You need to
call the store's dispatch function to actually cause the mutation. Sometimes we say
bound action creators to mean functions that call an action creator and immediately
dispatch its result to a specific store instance.
If an action creator needs to read the current state, perform an API call, or cause a side
effect, like a routing transition, it should return an async action instead of an action.
Async Action
type AsyncAction = any
An async action is a value that is sent to a dispatching function, but is not yet ready for
consumption by the reducer. It will be transformed by middleware into an action (or a
series of actions) before being sent to the base dispatch() function. Async actions may
have different types, depending on the middleware you use. They are often
asynchronous primitives, like a Promise or a thunk, which are not passed to the reducer
immediately, but trigger action dispatches once an operation has completed.
Middleware
type MiddlewareAPI = { dispatch: Dispatch, getState: () => State }
type Middleware = (api: MiddlewareAPI) => (next: Dispatch) => Dispatch
291
Glossary
Store
type Store = {
dispatch: Dispatch
getState: () => State
subscribe: (listener: () => void) => () => void
replaceReducer: (reducer: Reducer) => void
}
There should only be a single store in a Redux app, as the composition happens on the
reducer level.
Store creator
type StoreCreator = (reducer: Reducer, preloadedState: ?State) => Store
A store creator is a function that creates a Redux store. Like with dispatching function,
we must distinguish the base store creator, createStore(reducer, preloadedState)
exported from the Redux package, from store creators that are returned from the store
enhancers.
Store enhancer
292
Glossary
Store enhancers are much the same concept as higher-order components in React,
which are also occasionally called component enhancers.
Most likely you'll never write a store enhancer, but you may use the one provided by the
developer tools. It is what makes time travel possible without the app being aware it is
happening. Amusingly, the Redux middleware implementation is itself a store enhancer.
293
API Reference
API Reference
The Redux API surface is tiny. Redux defines a set of contracts for you to implement
(such as reducers) and provides a few helper functions to tie these contracts together.
This section documents the complete Redux API. Keep in mind that Redux is only
concerned with managing the state. In a real app, you'll also want to use UI bindings like
react-redux.
Top-Level Exports
createStore(reducer, [preloadedState], [enhancer])
combineReducers(reducers)
applyMiddleware(...middlewares)
bindActionCreators(actionCreators, dispatch)
compose(...functions)
Store API
Store
getState()
dispatch(action)
subscribe(listener)
replaceReducer(nextReducer)
Importing
Every function described above is a top-level export. You can import any of them like
this:
ES6
ES5 (CommonJS)
294
API Reference
295
createStore
createStore(reducer, [preloadedState],
[enhancer])
Creates a Redux store that holds the complete state tree of your app.
Arguments
1. reducer (Function): A reducing function that returns the next state tree, given the
2. [ preloadedState ] (any): The initial state. You may optionally specify it to hydrate the
state from the server in universal apps, or to restore a previously serialized user
session. If you produced reducer with combineReducers , this must be a plain object
with the same shape as the keys passed to it. Otherwise, you are free to pass
anything that your reducer can understand.
Returns
( Store ): An object that holds the complete state of your app. The only way to change its
state is by dispatching actions. You may also subscribe to the changes to its state to
update the UI.
Example
296
createStore
store.dispatch({
type: 'ADD_TODO',
text: 'Read the docs'
})
console.log(store.getState())
// [ 'Use Redux', 'Read the docs' ]
Tips
Don't create more than one store in an application! Instead, use combineReducers to
create a single root reducer out of many.
It is up to you to choose the state format. You can use plain objects or something
like Immutable. If you're not sure, start with plain objects.
If your state is a plain object, make sure you never mutate it! For example, instead
of returning something like Object.assign(state, newData) from your reducers,
return Object.assign({}, state, newData) . This way you don't override the previous
state . You can also write return { ...state, ...newData } if you enable the
For universal apps that run on the server, create a store instance with every request
so that they are isolated. Dispatch a few data fetching actions to a store instance
and wait for them to complete before rendering the app on the server.
297
createStore
298
Store
Store
A store holds the whole state tree of your application.
The only way to change the state inside it is to dispatch an action on it.
A store is not a class. It's just an object with a few methods on it.
If you're coming from Flux, there is a single important difference you need to
understand. Redux doesn't have a Dispatcher or support many stores. Instead,
there is just a single store with a single root reducing function. As your app
grows, instead of adding stores, you split the root reducer into smaller reducers
independently operating on the different parts of the state tree. You can use a
helper like combineReducers to combine them. This is similar to how there is just
one root component in a React app, but it is composed out of many small
components.
Store Methods
getState()
dispatch(action)
subscribe(listener)
replaceReducer(nextReducer)
Store Methods
getState()
Returns the current state tree of your application.
Returns
299
Store
dispatch(action)
Dispatches an action. This is the only way to trigger a state change.
The store's reducing function will be called with the current getState() result and the
given action synchronously. Its return value will be considered the next state. It will be
returned from getState() from now on, and the change listeners will immediately be
notified.
If you attempt to call dispatch from inside the reducer, it will throw with an error
saying Reducers may not dispatch actions. This is similar to Cannot dispatch in
a middle of dispatch error in Flux, but doesn't cause the problems associated with
it. In Flux, a dispatch is forbidden while Stores are handling the action and
emitting updates. This is unfortunate because it makes it impossible to dispatch
actions from component lifecycle hooks or other benign places.
In Redux, subscriptions are called after the root reducer has returned the new
state, so you may dispatch in the subscription listeners. You are only disallowed to
dispatch inside the reducers because they must have no side effects. If you want
to cause a side effect in response to an action, the right place to do this is in the
potentially async action creator.
Arguments
): A plain object describing the change that makes sense for your
1. action (Object
application. Actions are the only way to get data into the store, so any data, whether
from the UI events, network callbacks, or other sources such as WebSockets needs
to eventually be dispatched as actions. Actions must have a type field that
indicates the type of action being performed. Types can be defined as constants
and imported from another module. It's better to use strings for type than Symbols
because strings are serializable. Other than type , the structure of an action object
is really up to you. If you're interested, check out Flux Standard Action for
recommendations on how actions could be constructed.
Returns
300
Store
Notes
The vanilla store implementation you get by calling createStore only supports plain
object actions and hands them immediately to the reducer.
However, if you wrap createStore with applyMiddleware , the middleware can interpret
actions differently, and provide support for dispatching async actions. Async actions are
usually asynchronous primitives like Promises, Observables, or thunks.
Middleware is created by the community and does not ship with Redux by default. You
need to explicitly install packages like redux-thunk or redux-promise to use it. You may
also create your own middleware.
To learn how to describe asynchronous API calls, read the current state inside action
creators, perform side effects, or chain them to execute in a sequence, see the
examples for applyMiddleware .
Example
function addTodo(text) {
return {
type: 'ADD_TODO',
text
}
}
subscribe(listener)
Adds a change listener. It will be called any time an action is dispatched, and some part
of the state tree may potentially have changed. You may then call getState() to read
the current state tree inside the callback.
You may call dispatch() from a change listener, with the following caveats:
301
Store
1. The listener should only call dispatch() either in response to user actions or under
specific conditions (e. g. dispatching an action when the store has a specific field).
Calling dispatch() without any conditions is technically possible, however it leads
to infinite loop as every dispatch() call usually triggers the listener again.
2. The subscriptions are snapshotted just before every dispatch() call. If you
subscribe or unsubscribe while the listeners are being invoked, this will not have
any effect on the dispatch() that is currently in progress. However, the next
dispatch() call, whether nested or not, will use a more recent snapshot of the
subscription list.
3. The listener should not expect to see all state changes, as the state might have
been updated multiple times during a nested dispatch() before the listener is
called. It is, however, guaranteed that all subscribers registered before the
dispatch() started will be called with the latest state by the time it exits.
It is a low-level API. Most likely, instead of using it directly, you'll use React (or other)
bindings. If you commonly use the callback as a hook to react to state changes, you
might want to write a custom observeStore utility. The Store is also an Observable ,
so you can subscribe to changes with libraries like RxJS.
Arguments
1. listener (Function): The callback to be invoked any time an action has been
dispatched, and the state tree might have changed. You may call getState()
inside this callback to read the current state tree. It is reasonable to expect that the
store's reducer is a pure function, so you may compare references to some deep
path in the state tree to learn whether its value has changed.
Returns
Example
302
Store
function select(state) {
return state.some.deep.property
}
let currentValue
function handleChange() {
let previousValue = currentValue
currentValue = select(store.getState())
replaceReducer(nextReducer)
Replaces the reducer currently used by the store to calculate the state.
It is an advanced API. You might need this if your app implements code splitting, and
you want to load some of the reducers dynamically. You might also need this if you
implement a hot reloading mechanism for Redux.
Arguments
1. reducer (Function) The next reducer for the store to use.
303
combineReducers
combineReducers(reducers)
As your app grows more complex, you'll want to split your reducing function into
separate functions, each managing independent parts of the state.
The combineReducers helper function turns an object whose values are different
reducing functions into a single reducing function you can pass to createStore .
The resulting reducer calls every child reducer, and gathers their results into a single
state object. The shape of the state object matches the keys of the passed
reducers .
{
reducer1: ...
reducer2: ...
}
You can control state key names by using different keys for the reducers in the passed
object. For example, you may call combineReducers({ todos: myTodosReducer, counter:
myCounterReducer }) for the state shape to be { todos, counter } .
A popular convention is to name reducers after the state slices they manage, so you can
use ES6 property shorthand notation: combineReducers({ counter, todos }) . This is
equivalent to writing combineReducers({ counter: counter, todos: todos }) .
This function helps you organize your reducers to manage their own slices of
state, similar to how you would have different Flux Stores to manage different
state. With Redux, there is just one store, but combineReducers helps you keep
the same logical division between reducers.
Arguments
1. reducers (Object): An object whose values correspond to different reducing
functions that need to be combined into one. See the notes below for some rules
every passed reducer must follow.
304
combineReducers
below.
Returns
(Function): A reducer that invokes every reducer inside the reducers object, and
constructs a state object with the same shape.
Notes
This function is mildly opinionated and is skewed towards helping beginners avoid
common pitfalls. This is why it attempts to enforce some rules that you don't have to
follow if you write the root reducer manually.
For any action that is not recognized, it must return the state given to it as the first
argument.
It must never return undefined . It is too easy to do this by mistake via an early
return statement, so combineReducers throws if you do that instead of letting the
If the state given to it is undefined , it must return the initial state for this specific
reducer. According to the previous rule, the initial state must not be undefined
either. It is handy to specify it with ES6 optional arguments syntax, but you can also
explicitly check the first argument for being undefined .
While combineReducers attempts to check that your reducers conform to some of these
rules, you should remember them, and do your best to follow them. combineReducers will
check your reducers by passing undefined to them; this is done even if you specify
initial state to Redux.createStore(combineReducers(...), initialState) . Therefore, you
must ensure your reducers work properly when receiving undefined as state, even if
you never intend for them to actually receive undefined in your own code.
Example
reducers/todos.js
305
combineReducers
reducers/counter.js
reducers/index.js
App.js
306
combineReducers
store.dispatch({
type: 'ADD_TODO',
text: 'Use Redux'
})
console.log(store.getState())
// {
// counter: 0,
// todos: [ 'Use Redux' ]
// }
Tips
This helper is just a convenience! You can write your own combineReducers that
works differently, or even assemble the state object from the child reducers
manually and write a root reducing function explicitly, like you would write any other
function.
You may call combineReducers at any level of the reducer hierarchy. It doesn't have
to happen at the top. In fact you may use it again to split the child reducers that get
too complicated into independent grandchildren, and so on.
307
applyMiddleware
applyMiddleware(...middlewares)
Middleware is the suggested way to extend Redux with custom functionality. Middleware
lets you wrap the store's dispatch method for fun and profit. The key feature of
middleware is that it is composable. Multiple middleware can be combined together,
where each middleware requires no knowledge of what comes before or after it in the
chain.
The most common use case for middleware is to support asynchronous actions without
much boilerplate code or a dependency on a library like Rx. It does so by letting you
dispatch async actions in addition to normal actions.
For example, redux-thunk lets the action creators invert control by dispatching functions.
They would receive dispatch as an argument and may call it asynchronously. Such
functions are called thunks. Another example of middleware is redux-promise. It lets you
dispatch a Promise async action, and dispatches a normal action when the Promise
resolves.
Middleware is not baked into createStore and is not a fundamental part of the Redux
architecture, but we consider it useful enough to be supported right in the core. This
way, there is a single standard way to extend dispatch in the ecosystem, and different
middleware may compete in expressiveness and utility.
Arguments
...middlewares (arguments): Functions that conform to the Redux middleware API.
Each middleware receives Store 's dispatch and getState functions as named
arguments, and returns a function. That function will be given the next
middleware's dispatch method, and is expected to return a function of action
calling next(action) with a potentially different argument, or at a different time, or
maybe not calling it at all. The last middleware in the chain will receive the real
store's dispatch method as the next parameter, thus ending the chain. So, the
middleware signature is ({ getState, dispatch }) => next => action .
Returns
308
applyMiddleware
(Function) A store enhancer that applies the given middleware. The store enhancer
signature is createStore => createStore' but the easiest way to apply it is to pass it to
createStore() as the last enhancer argument.
store.dispatch({
type: 'ADD_TODO',
text: 'Understand the middleware'
})
// (These lines will be logged by the middleware:)
// will dispatch: { type: 'ADD_TODO', text: 'Understand the middleware' }
// state after dispatch: [ 'Use Redux', 'Understand the middleware' ]
function fetchSecretSauce() {
return fetch('https://www.google.com/search?q=secret+sauce')
309
applyMiddleware
// These are the normal action creators you have seen so far.
// The actions they return can be dispatched without any middleware.
// However, they only express facts and not the async flow.
function withdrawMoney(amount) {
return {
type: 'WITHDRAW',
amount
}
}
// Meet thunks.
// A thunk is a function that returns a function.
// This is a thunk.
function makeASandwichWithSecretSauce(forPerson) {
// Invert control!
// Return a function that accepts `dispatch` so we can dispatch later.
// Thunk middleware knows how to turn thunk async actions into actions.
310
applyMiddleware
store.dispatch(
makeASandwichWithSecretSauce('Me')
)
store.dispatch(
makeASandwichWithSecretSauce('My wife')
).then(() => {
console.log('Done!')
})
function makeSandwichesForEverybody() {
return function (dispatch, getState) {
if (!getState().sandwiches.isShopOpen) {
return Promise.resolve()
}
return dispatch(
makeASandwichWithSecretSauce('My Grandma')
).then(() =>
Promise.all([
dispatch(makeASandwichWithSecretSauce('Me')),
dispatch(makeASandwichWithSecretSauce('My wife'))
])
).then(() =>
dispatch(makeASandwichWithSecretSauce('Our kids'))
).then(() =>
dispatch(getState().myMoney > 42 ?
withdrawMoney(42) :
apologize('Me', 'The Sandwich Shop')
)
)
}
}
// This is very useful for server side rendering, because I can wait
// until data is available, then synchronously render the app.
store.dispatch(
makeSandwichesForEverybody()
311
applyMiddleware
).then(() =>
response.send(renderToString(<MyApp store={store} />))
)
componentWillReceiveProps(nextProps) {
if (nextProps.forPerson !== this.props.forPerson) {
this.props.dispatch(
makeASandwichWithSecretSauce(nextProps.forPerson)
)
}
}
render() {
return <p>{this.props.sandwiches.join('mustard')}</p>
}
}
Tips
Middleware only wraps the store's dispatch function. Technically, anything a
middleware can do, you can do manually by wrapping every dispatch call, but it's
easier to manage this in a single place and define action transformations on the
scale of the whole project.
If you use other store enhancers in addition to applyMiddleware , make sure to put
applyMiddleware before them in the composition chain because the middleware is
312
applyMiddleware
If you want to conditionally apply a middleware, make sure to only import it when it's
needed:
let middleware = [ a, b ]
if (process.env.NODE_ENV !== 'production') {
let c = require('some-debug-middleware')
let d = require('another-debug-middleware')
middleware = [ ...middleware, c, d ]
}
This makes it easier for bundling tools to cut out unneeded modules and reduces
the size of your builds.
Middleware sounds much more complicated than it really is. The only way to really
understand middleware is to see how the existing middleware works, and try to write
your own. The function nesting can be intimidating, but most of the middleware
you'll find are, in fact, 10-liners, and the nesting and composability is what makes
the middleware system powerful.
313
bindActionCreators
bindActionCreators(actionCreators,
dispatch)
Turns an object whose values are action creators, into an object with the same keys, but
with every action creator wrapped into a dispatch call so they may be invoked directly.
Normally you should just call dispatch directly on your Store instance. If you use
Redux with React, react-redux will provide you with the dispatch function so you can
call it directly, too.
The only use case for bindActionCreators is when you want to pass some action
creators down to a component that isn't aware of Redux, and you don't want to pass
dispatch or the Redux store to it.
For convenience, you can also pass a single function as the first argument, and get a
function in return.
Parameters
1. actionCreators (Function or Object): An action creator, or an object whose values
Returns
(Function or Object): An object mimicking the original object, but with each function
immediately dispatching the action returned by the corresponding action creator. If you
passed a function as actionCreators , the return value will also be a single function.
Example
TodoActionCreators.js
314
bindActionCreators
SomeComponent.js
render() {
// Injected by react-redux:
let { todos, dispatch } = this.props
315
bindActionCreators
// {
// addTodo: Function,
// removeTodo: Function
// }
return (
<TodoList todos={todos}
{...boundActionCreators} />
)
Tips
You might ask: why don't we bind the action creators to the store instance right
away, like in classical Flux? The problem is that this won't work well with universal
apps that need to render on the server. Most likely you want to have a separate
store instance per request so you can prepare them with different data, but binding
action creators during their definition means you're stuck with a single store instance
for all requests.
If you use ES5, instead of import * as syntax you can just pass
require('./TodoActionCreators') to bindActionCreators as the first argument. The
only thing it cares about is that the values of the actionCreators arguments are
functions. The module system doesn't matter.
316
compose
compose(...functions)
Composes functions from right to left.
Arguments
1. (arguments): The functions to compose. Each function is expected to accept a
single parameter. Its return value will be provided as an argument to the function
standing to the left, and so on. The exception is the right-most argument which can
accept multiple parameters, as it will provide the signature for the resulting
composed function.
Returns
(Function): The final function obtained by composing the given functions from right to
left.
Example
This example demonstrates how to use compose to enhance a store with
applyMiddleware and a few developer tools from the redux-devtools package.
Tips
317
compose
All compose does is let you write deeply nested function transformations without the
rightward drift of the code. Don't give it too much credit!
318
Change Log
Change Log
This project adheres to Semantic Versioning.
Every release, along with the migration instructions, is documented on the Github
Releases page.
319
Patrons
Patrons
The work on Redux was funded by the community.
Meet some of the outstanding companies and individuals that made it possible:
Webflow
Ximedes
HauteLook
Ken Wheeler
Chung Yen Li
Sunil Pai
Charlie Cheever
Eugene G
Matt Apperson
Jed Watson
Sasha Aickin
Stefan Tennigkeit
Sam Vincent
Olegzandr Denman
320
Feedback
Feedback
We appreciate feedback from the community. You can post feature requests and bug
reports on Product Pains.
Recent
321
Feedback
2 Action amplification t 0
Load
more
322