createStore(reducer, [preloadedState], [enhancer])
Creates a Redux [store] that holds the complete state tree of your app.
There should only be a single store in your app.
Arguments
reducer
(Function): A [reducing function] that returns the next [state tree], given the current state tree and an [action] to handle.[
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 producedreducer
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 yourreducer
can understand.[
enhancer
] (Function): The store enhancer. You may optionally specify it to enhance the store with third-party capabilities such as middleware, time travel, persistence, etc. The only store enhancer that ships with Redux is [applyMiddleware()
].
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
import { createStore } from 'redux'
function todos(state = [], action) {
switch (action.type) {
case 'ADD_TODO':
return state.concat([ action.text ])
default:
return state
}
}
let store = createStore(todos, [ 'Use Redux' ])
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, returnObject.assign({}, state, newData)
. This way you don't override the previousstate
. You can also writereturn { ...state, ...newData }
if you enable the [object spread operator proposal].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.
When a store is created, Redux dispatches a dummy action to your reducer to populate the store with the initial state. You are not meant to handle the dummy action directly. Just remember that your reducer should return some kind of initial state if the state given to it as the first argument is
undefined
, and you're all set.To apply multiple store enhancers, you may use [
compose()
].
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] in addition to normal actions.
For example, [redux-thunk] async action, and dispatches a normal action when the Promise resolves.
Middleware is not baked into [createStore
] 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
] => next => action`.
Returns
(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.
Example: Custom Logger Middleware
import { createStore, applyMiddleware } from 'redux'
import todos from './reducers'
function logger({ getState }) {
return (next) => (action) => {
console.log('will dispatch', action)
// Call the next dispatch method in the middleware chain.
let returnValue = next(action)
console.log('state after dispatch', getState())
// This will likely be the action itself, unless
// a middleware further in chain changed it.
return returnValue
}
}
let store = createStore(
todos,
[ 'Use Redux' ],
applyMiddleware(logger)
)
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' ]
Example: Using Thunk Middleware for Async Actions
import { createStore, combineReducers, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import * as reducers from './reducers'
let reducer = combineReducers(reducers)
// applyMiddleware supercharges createStore with middleware:
let store = createStore(reducer, applyMiddleware(thunk))
function fetchSecretSauce() {
return fetch('https://www.google.com/search?q=secret+sauce')
}
// 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 makeASandwich(forPerson, secretSauce) {
return {
type: 'MAKE_SANDWICH',
forPerson,
secretSauce
}
}
function apologize(fromPerson, toPerson, error) {
return {
type: 'APOLOGIZE',
fromPerson,
toPerson,
error
}
}
function withdrawMoney(amount) {
return {
type: 'WITHDRAW',
amount
}
}
// Even without middleware, you can dispatch an action:
store.dispatch(withdrawMoney(100))
// But what do you do when you need to start an asynchronous action,
// such as an API call, or a router transition?
// 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.
return function (dispatch) {
return fetchSecretSauce().then(
sauce => dispatch(makeASandwich(forPerson, sauce)),
error => dispatch(apologize('The Sandwich Shop', forPerson, error))
)
}
}
// Thunk middleware lets me dispatch thunk async actions
// as if they were actions!
store.dispatch(
makeASandwichWithSecretSauce('Me')
)
// It even takes care to return the thunk's return value
// from the dispatch, so I can chain Promises as long as I return them.
store.dispatch(
makeASandwichWithSecretSauce('My wife')
).then(() => {
console.log('Done!')
})
// In fact I can write action creators that dispatch
// actions and async actions from other action creators,
// and I can build my control flow with Promises.
function makeSandwichesForEverybody() {
return function (dispatch, getState) {
if (!getState().sandwiches.isShopOpen) {
// You don't have to return Promises, but it's a handy convention
// so the caller can always call .then() on async dispatch result.
return Promise.resolve()
}
// We can dispatch both plain object actions and other thunks,
// which lets us compose the asynchronous actions in a single flow.
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.
import { renderToString } from 'react-dom/server'
store.dispatch(
makeSandwichesForEverybody()
).then(() =>
response.send(renderToString(<MyApp store={store} />))
)
// I can also dispatch a thunk async action from a component
// any time its props change to load the missing data.
import { connect } from 'react-redux'
import { Component } from 'react'
class SandwichShop extends Component {
componentDidMount() {
this.props.dispatch(
makeASandwichWithSecretSauce(this.props.forPerson)
)
}
componentWillReceiveProps(nextProps) {
if (nextProps.forPerson !== this.props.forPerson) {
this.props.dispatch(
makeASandwichWithSecretSauce(nextProps.forPerson)
)
}
}
render() {
return <p>{this.props.sandwiches.join('mustard')}</p>
}
}
export default connect(
state => ({
sandwiches: state.sandwiches
})
)(SandwichShop)
Tips
Middleware only wraps the store's [
dispatch
] function. Technically, anything a middleware can do, you can do manually by wrapping everydispatch
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 putapplyMiddleware
before them in the composition chain because the middleware is potentially asynchronous. For example, it should go before [redux-devtools] because otherwise the DevTools won't see the raw actions emitted by the Promise middleware and such.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 ] } const store = createStore( reducer, preloadedState, applyMiddleware(...middleware) )
This makes it easier for bundling tools to cut out unneeded modules and reduces the size of your builds.
Ever wondered what
applyMiddleware
itself is? It ought to be an extension mechanism more powerful than the middleware itself. Indeed,applyMiddleware
is an example of the most powerful Redux extension mechanism called [store enhancers]. Middleware is less powerful than a store enhancer, but it is easier to write.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.
To apply multiple store enhancers, you may use [
compose()
].
compose(...functions)
Composes functions from right to left.
This is a functional programming utility, and is included in Redux as a convenience.
You might want to use it to apply several [store enhancers] in a row.
Arguments
- (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] package.
import { createStore, combineReducers, applyMiddleware, compose } from 'redux'
import thunk from 'redux-thunk'
import DevTools from './containers/DevTools'
import reducer from '../reducers/index'
const store = createStore(
reducer,
compose(
applyMiddleware(thunk),
DevTools.instrument()
)
)
Tips
- 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!