一个可预测的全局状态管理的 JS 库
A JS library for predictable global state management
redux
=========================
[![build status](https://img.shields.io/travis/gaearon/redux.svg?style=flat-square)](https://travis-ci.org/gaearon/redux)
[![npm version](https://img.shields.io/npm/v/redux.svg?style=flat-square)](https://www.npmjs.com/package/redux)
Atomic Flux with hot reloading.
**The API is likely to change a few times before we reach 1.0.**
**Its [surface area](http://www.youtube.com/watch?v=4anAwXYqLG8) is minimal so you can try it in production and report any issues.**
# Table of Contents
- [Why another Flux framework?](#why-another-flux-framework)
- [Philosophy & Design Goals](#philosophy--design-goals)
- [Demo](#demo)
- [Running TodoMVC](#running-todomvc)
- [What does it look like?](#what-does-it-look-like)
- [Actions](#actions)
- [Stores](#stores)
- [Components](#components)
- [Dumb Components](#dumb-components)
- [Smart Components](#smart-components)
- [Decorators](#decorators)
- [React Native](#react-native)
- [Initializing Redux](#initializing-redux)
- [Running the same code on client and server](#running-the-same-code-on-client-and-server)
- [Additional customization](#additional-customization)
- [FAQ](#faq)
- [Any examples with data fetching and `react-router`?](#any-examples-with-data-fetching-and-react-router)
- [How does hot reloading work?](#how-does-hot-reloading-work)
- [Can I use this in production?](#can-i-use-this-in-production)
- [How do I do async?](#how-do-i-do-async)
- [But there are switch statements!](#but-there-are-switch-statements)
- [What about `waitFor`?](#what-about-waitfor)
- [My views aren't updating!](#my-views-arent-updating)
- [How do Stores, Actions and Components interact?](#how-do-stores-actions-and-components-interact)
- [Discussion](#discussion)
- [Inspiration and Thanks](#inspiration-and-thanks)
## Why another Flux framework?
Read **[The Evolution of Flux Frameworks](https://medium.com/@dan_abramov/the-evolution-of-flux-frameworks-6c16ad26bb31)** for some context.
### Philosophy & Design Goals
* You shouldn't need a book on functional programming to use Redux.
* Everything (Stores, Action Creators, configuration) is hot reloadable.
* Preserves the benefits of Flux, but adds other nice properties thanks to its functional nature.
* Prevents some of the anti-patterns common in Flux code.
* Works great in isomoprhic apps because it doesn't use singletons and the data can be rehydrated.
* Doesn't care how you store your data: you may use JS objects, arrays, ImmutableJS, etc.
* Under the hood, it keeps all your data in a tree, but you don't need to think about it.
* Lets you efficiently subscribe to finer-grained updates than individual Stores.
* Provides hooks for powerful devtools (e.g. time travel, record/replay) to be implementable without user buy-in.
* Provides extension points so it's easy to [support promises](https://github.com/gaearon/redux/issues/99#issuecomment-112212639) or [generate constants](https://gist.github.com/skevy/8a4ffc3cfdaf5fd68739) outside the core.
* No wrapper calls in your stores and actions. Your stuff is your stuff.
* It's super easy to test things in isolation without mocks.
* You can use “flat” Stores, or [compose and reuse Stores](https://gist.github.com/gaearon/d77ca812015c0356654f) just like you compose Components.
* The API surface area is minimal.
* Have I mentioned hot reloading yet?
## Demo
## Running TodoMVC
```
git clone https://github.com/gaearon/redux.git redux
cd redux
npm install
cd examples/todomvc
npm install
npm start
```
## What does it look like?
### Actions
```js
// Still using constants...
import { INCREMENT_COUNTER, DECREMENT_COUNTER } from '../constants/ActionTypes';
// But action creators are pure functions returning actions
export function increment() {
return {
type: INCREMENT_COUNTER
};
}
export function decrement() {
return {
type: DECREMENT_COUNTER
};
}
// Can also be async if you return a function
export function incrementAsync() {
return dispatch => {
setTimeout(() => {
// Yay! Can invoke sync or async actions with `dispatch`
dispatch(increment());
}, 1000);
};
}
// Could also read state of a store in the callback form
export function incrementIfOdd() {
return (dispatch, getState) => {
const { counter } = getState();
if (counter % 2 === 0) {
return;
}
dispatch(increment());
};
}
```
### Stores
```js
// ... too, use constants
import { INCREMENT_COUNTER, DECREMENT_COUNTER } from '../constants/ActionTypes';
// what's important is that Store is a pure function,
// and you can write it anyhow you like.
// the Store signature is (state, action) => state,
// and the state shape is up to you: you can use primitives,
// objects, arrays, or even ImmutableJS objects.
export default function counter(state = 0, action) {
// this function returns the new state when an action comes
switch (action.type) {
case INCREMENT_COUNTER:
return state + 1;
case DECREMENT_COUNTER:
return state - 1;
default:
return state;
}
// BUT THAT'S A SWITCH STATEMENT!
// Right. If you hate 'em, see the FAQ below.
}
```
### Components
#### Dumb Components
```js
// The dumb component receives everything using props:
import React, { PropTypes } from 'react';
export default class Counter {
static propTypes = {
increment: PropTypes.func.isRequired,
decrement: PropTypes.func.isRequired,
counter: PropTypes.number.isRequired
};
render() {
const { increment, decrement, counter } = this.props;
return (
Clicked: {counter} times
{' '}
{' '}
);
}
}
```
#### Smart Components
```js
// The smart component may observe stores using ``,
// and bind actions to the dispatcher with `bindActionCreators`.
import React from 'react';
import { bindActionCreators } from 'redux';
import { Connector } from 'redux/react';
import Counter from '../components/Counter';
import * as CounterActions from '../actions/CounterActions';
// You can optionally specify `select` for finer-grained subscriptions
// and retrieval. Only when the return value is shallowly different,
// will the child component be updated.
function select(state) {
return { counter: state.counter };
}
export default class CounterApp {
render() {
return (
{({ counter, dispatch }) =>
/* Yes this is child as a function. */
}
);
}
}
```
#### Decorators
The `@connect` decorator lets you create smart components less verbosely:
```js
import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'redux/react';
import Counter from '../components/Counter';
import * as CounterActions from '../actions/CounterActions';
@connect(state => ({
counter: state.counter
}))
export default class CounterApp {
render() {
const { counter, dispatch } = this.props;
// Instead of `bindActionCreators`, you may also pass `dispatch` as a prop
// to your component and call `dispatch(CounterActions.increment())`
return (
);
}
}
```
### React Native
To use Redux with React Native, just replace imports from `redux/react` with `redux/react-native`:
```js
import { bindActionCreators } from 'redux';
import { Provider, Connector } from 'redux/react-native';
```
### Initializing Redux
The simplest way to initialize a Redux instance is to give it an object whose values are your Store functions, and whose keys are the