From cc6700b2b73d2d03f72c5e03e016f2e8bdc6babc Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 11:00:46 +0300 Subject: [PATCH 01/85] Meh --- README.md | 497 +-------------------------------------------- docs/middleware.md | 86 -------- 2 files changed, 7 insertions(+), 576 deletions(-) delete mode 100644 docs/middleware.md diff --git a/README.md b/README.md index 15a7d78f95..499951ebec 100644 --- a/README.md +++ b/README.md @@ -5,499 +5,16 @@ redux [![npm version](https://img.shields.io/npm/v/redux.svg?style=flat-square)](https://www.npmjs.com/package/redux) [![redux channel on slack](https://img.shields.io/badge/slack-redux@reactiflux-61DAFB.svg?style=flat-square)](http://www.reactiflux.com) -Atomic Flux with hot reloading. +Redux is an architecture for JavaScript applications with unidirectional data flow. It evolves the ideas of [Flux](https://facebook.github.io/flux), taking cues from [Elm](elm-lang.org/guide/architecture) and [Om](http://swannodette.github.io/2013/12/17/the-future-of-javascript-mvcs/) for a succinct approach based on functional composition. -**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.** +Redux works both for client-side, universal, and native apps. You can use Redux together with [React](https://facebook.github.io/react/) or any other view library. Redux is tiny (2kB) and has no dependencies, but its ecosystem takes the developer experience to the next level. -**You can track the [new docs](https://github.com/gaearon/redux/pull/140) and the [1.0 API and terminology changes](https://github.com/gaearon/redux/pull/195).** +## How It Works +Redux can be described in three fundamental principles: -# Table of Contents +* **The whole state of your app is stored in an object tree inside a single *store*.** This makes it easy to create universal apps. The state from the server can be serialized and hydrated into the client with no extra coding effort. You can also persist your app’s state in development for a faster development cycle. And of course, with a single state tree, you get the previously difficult functionality like Undo/Redo for free. -- [Why another Flux framework?](#why-another-flux-framework) - - [Philosophy & Design Goals](#philosophy--design-goals) -- [The Talk](#the-talk) -- [Demo](#demo) -- [Examples](#examples) - - [Simple Examples](#simple-examples) - - [ES5 Examples](#es5-examples) - - [Async and Universal Examples with Routing](#async-and-universal-examples-with-routing) -- [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) - - [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) +* **The only way to mutate the state is to emit an *action*, an object describing what happened.** This ensures that the views or the network callbacks never write directly to the state, and instead express the intent to mutate. Because all mutations are centralized and happen one by one in a strict order, there are no subtle race conditions to watch out for. Actions are just plain objects, so they can be logged, serialized, stored, and later replayed for debugging or testing purposes. -## 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 [universal (aka “isomorphic”)](https://medium.com/@mjackson/universal-javascript-4761051b7ae9) 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? - -## The Talk - -Redux was demoed together with **[React Hot Loader](https://github.com/gaearon/react-hot-loader)** at React Europe. -Watch **[Dan Abramov's talk on Hot Reloading with Time Travel](https://www.youtube.com/watch?v=xsSnOQynTHs).** - -## Demo - - - -## Examples - -### Simple Examples - -Redux is distributed with a Counter and a TodoMVC example in its source code. - -First, clone the repo: - -``` -git clone https://github.com/gaearon/redux.git -cd redux -``` - -Run the Counter example: - -``` -cd redux/examples/counter -npm install -npm start -``` - -Run the TodoMVC example: - -``` -cd ../todomvc -npm install -npm start -``` - -### ES5 Examples - -If you have not used ES6 before, check out one of these ES5 examples: - -* [redux-todomvc-es5](https://github.com/insin/redux-todomvc-es5) - -### Async and Universal Examples with Routing - -These async and [universal (aka “isomorphic”)](https://medium.com/@mjackson/universal-javascript-4761051b7ae9) examples using React Router should help you get started: - -* [redux-react-router-async-example](https://github.com/emmenko/redux-react-router-async-example): Work in progress. Semi-official. Only the client side. Uses React Router. -* [react-redux-universal-hot-example](https://github.com/erikras/react-redux-universal-hot-example): Universal. Uses React Router. -* [redux-example](https://github.com/quangbuule/redux-example): Universal. Uses Immutable, React Router. -* [isomorphic-counter-example](https://github.com/khtdr/redux-react-koa-isomorphic-counter-example): Universal. A bare-bone implementation of the [counter example app](https://github.com/gaearon/redux/tree/master/examples/counter). Uses promises-middleware to interact with API via Koa on the server. -* [Awesome list](https://github.com/xgrommx/awesome-redux) - -Don’t be shy, add your own! - -## 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 their names. You may `import *` from the file with all your Store definitions to obtain such an object: - -```js -import { createRedux } from 'redux'; -import { Provider } from 'redux/react'; -import * as stores from '../stores/index'; - -const redux = createRedux(stores); -``` - -Then pass `redux` as a prop to `` component in the root component of your app, and you're all set: - -```js -export default class App { - render() { - return ( - - {() => - - } - - ); - } -} -``` - -### Running the same code on client and server - -The `redux` instance returned by `createRedux` also has the `dispatch(action)`, `subscribe()` and `getState()` methods that you may call outside the React components. - -You may optionally specify the initial state as the second argument to `createRedux`. This is useful for hydrating the state you received from running Redux on the server: - -```js -// server -const redux = createRedux(stores); -redux.dispatch(MyActionCreators.doSomething()); // fire action creators to fill the state -const state = redux.getState(); // somehow pass this state to the client - -// client -const initialState = window.STATE_FROM_SERVER; -const redux = createRedux(stores, initialState); -``` - -### Additional customization - -There is also a longer way to do the same thing, if you need additional customization. - -This: - -```js -import { createRedux } from 'redux'; -import * as stores from '../stores/index'; - -const redux = createRedux(stores); -``` - -is in fact a shortcut for this: - -```js -import { createRedux, createDispatcher, composeStores } from 'redux'; -import thunkMiddleware from 'redux/lib/middleware/thunk'; -import * as stores from '../stores/index'; - -// Compose all your Stores into a single Store function with `composeStores`: -const store = composeStores(stores); - -// Create a Dispatcher function for your composite Store: -const dispatcher = createDispatcher( - store, - getState => [thunkMiddleware(getState)] // Pass the default middleware -); - -// Create a Redux instance using the dispatcher function: -const redux = createRedux(dispatcher); -``` - -Why would you want to write it longer? Maybe you're an advanced user and want to provide a custom Dispatcher function, or maybe you have a different idea of how to compose your Stores (or you're satisfied with a single Store). Redux lets you do all of this. - -`createDispatcher()` also gives you the ability to specify middleware -- for example, to add support for promises. [Learn more](https://github.com/gaearon/redux/blob/master/docs/middleware.md) about how to create and use middleware in Redux. - -When in doubt, use the shorter option! - -## FAQ - -### How does hot reloading work? - -* http://webpack.github.io/docs/hot-module-replacement.html -* http://gaearon.github.io/react-hot-loader/ -* Literally that's it. Redux is fully driven by component props, so it works on top of React Hot Loader. - -### Can I use this in production? - -Yep. People already do that although I warned them! The API surface is minimal so migrating to 1.0 API when it comes out won't be difficult. Let us know about any issues. - -### How do I do async? - -There's already a built-in way of doing async action creators: - -```js -// 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); - }; -} -``` - -It's also easy to implement support for returning Promises or Observables with a custom middleware. [See an example of a custom Promise middleware.](https://github.com/gaearon/redux/issues/99#issuecomment-112212639) - -### But there are switch statements! - -`(state, action) => state` is as simple as a Store can get. You are free to implement your own `createStore`: - -```js -export default function createStore(initialState, handlers) { - return (state = initialState, action) => - handlers[action.type] ? - handlers[action.type](state, action) : - state; -} -``` - -and use it for your Stores: - -```js -export default createStore(0, { - [INCREMENT_COUNTER]: x => x + 1, - [DECREMENT_COUNTER]: x => x - 1 -}); -``` - -It's all just functions. -Fancy stuff like generating stores from handler maps, or generating action creator constants, should be in userland. -Redux has no opinion on how you do this in your project. - -See also [this gist](https://gist.github.com/skevy/8a4ffc3cfdaf5fd68739) for an example implementation of action constant generation. - -### What about `waitFor`? - -I wrote a lot of vanilla Flux code and my only use case for it was to avoid emitting a change before a related Store consumes the action. This doesn't matter in Redux because the change is only emitted after *all* Stores have consumed the action. - -If several of your Stores want to read data from each other and depend on each other, it's a sign that they should've been a single Store instead. [See this discussion on how `waitFor` can be replaced by the composition of stateless Stores.](https://gist.github.com/gaearon/d77ca812015c0356654f) - -### My views aren't updating! - -Redux makes a hard assumption that you never mutate the state passed to you. It's easy! For example, instead of - -```js -function (state, action) { - state.isAuthenticated = true; - state.email = action.email; - return state; -} -``` - -you should write - -```js -function (state, action) { - return { - ...state, - isAuthenticated: true, - email: action.email - }; -} -``` - -[Read more](https://github.com/sebmarkbage/ecmascript-rest-spread) about the spread properties ES7 proposal. - -### How do Stores, Actions and Components interact? - -Action creators are just pure functions so they don't interact with anything. Components need to call `dispatch(action)` (or use `bindActionCreators` that wraps it) to dispatch an action *returned* by the action creator. - -Stores are just pure functions too so they don't need to be “registered” in the traditional sense, and you can't subscribe to them directly. They're just descriptions of how data transforms. So in that sense they don't “interact” with anything either, they just exist, and are used by the dispatcher for computation of the next state. - -Now, the dispatcher is more interesting. You pass all the Stores to it, and it composes them into a single Store function that it uses for computation. The dispatcher is also a pure function, and it is passed as configuration to `createRedux`, the only stateful thing in Redux. By default, the default dispatcher is used, so if you call `createRedux(stores)`, it is created implicitly. - -To sum it up: there is a Redux instance at the root of your app. It binds everything together. It accepts a dispatcher (which itself accepts Stores), it holds the state, and it knows how to turn actions into state updates. Everything else (components, for example) subscribes to the Redux instance. If something wants to dispatch an action, they need to do it on the Redux instance. `Connector` is a handy shortcut for subscribing to a slice of the Redux instance's state and injecting `dispatch` into your components, but you don't have to use it. - -There is no other “interaction” in Redux. - -## Discussion - -Join the **#redux** channel of the [Reactiflux](http://reactiflux.com/) Slack community - -## Inspiration and Thanks - -* [Webpack](https://github.com/webpack/docs/wiki/hot-module-replacement-with-webpack) for Hot Module Replacement -* [The Elm Architecture](https://github.com/evancz/elm-architecture-tutorial) for a great intro to “stateless Stores” -* [Turning the database inside-out](http://blog.confluent.io/2015/03/04/turning-the-database-inside-out-with-apache-samza/) for blowing my mind -* [Developing ClojureScript with Figwheel](http://www.youtube.com/watch?v=j-kj2qwJa_E) for convincing me that re-evaluation should “just work” -* [Flummox](https://github.com/acdlite/flummox) for teaching me to approach Flux without boilerplate or singletons -* [disto](https://github.com/threepointone/disto) for a proof of concept of hot reloadable Stores -* [NuclearJS](https://github.com/optimizely/nuclear-js) for proving this architecture can be performant -* [Om](https://github.com/omcljs/om) for popularizing the idea of a single state atom -* [Cycle](https://github.com/staltz/cycle) for showing how often a function is the best tool -* [React](https://github.com/facebook/react) for the pragmatic innovation - -Special thanks go to [Jamie Paton](http://jdpaton.github.io/) for handing over the `redux` NPM package name. +* **To specify how different parts of the state tree are transformed by the actions, you write pure *reducers*.** Reducers are just pure functions that take the previous state and the action, and return the next state. You can start with a single reducer, but as your app grows, you can split it into smaller reducers that manage the 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. diff --git a/docs/middleware.md b/docs/middleware.md deleted file mode 100644 index 7de0ee8bce..0000000000 --- a/docs/middleware.md +++ /dev/null @@ -1,86 +0,0 @@ -# Middleware - -A middleware is a function that wraps the `dispatch()` method, or another middleware. For example: - -```js -// Instead of this -dispatch(action) -// do this -middleware(dispatch)(action) -``` - -Multiple middleware can be composed manually - -```js -middleware1(middleware2(dispatch))(action) -``` - -Or using the provided `composeMiddleware()` utility: - -```js -import { composeMiddleware } from 'redux'; - -// Equivalent: -middleware1(middleware2(dispatch))(action) -composeMiddleware(middleware1, middleware2, dispatch)(action) -``` - -`composeMiddleware` enables you to easily compose an array of middleware using spread notation: - -``` -composeMiddleware(...middlewares); -``` - -## Example of how to write middleware - -Here's a middleware for adding naive promise support to Redux: - -```js -function promiseMiddleware(next) { - return action => - action && typeof action.then === 'function' - ? action.then(next) - : next(action); -} -``` - -Pretty simple, right? Since they're just higher-order functions, most middleware will be very concise and easy to read. - -Note that `next` can be called as many times as needed, or not at all. - -## Use cases - -Often they'll be used like schedulers. They can be used to implement promise support (a la Flummox), observable support, generator support, whatever. Or they can be used for side-effects like logging. - - -## API - -Because middleware simply wraps `dispatch()` to return a function of the same signature, you can use them from the call site without any extra set up. (For the same reason, middleware is actually compatible with any Flux library with a `dispatch()` method.) - -However, for the most part, you'll want to configure middleware at the dispatcher level and apply them to every dispatch. - -For now, we are treating middleware as an advanced feature. You'll have to forgo the `createRedux(stores)` shortcut and create a dispatcher using `createDispatcher()`, which has an optional second parameter for configuring middleware. You can pass an array of middleware: - -```js -const dispatcher = createDispatcher( - composeStores(stores), - [promiseMiddleware, filterUndefined] -); -``` - -Or alternatively, you can pass a function that returns an array of middleware. The function accepts a single parameter, `getState()`, which enables the use of middleware that needs to access the current state on demand. For example, here's how the default dispatcher is created internally when you call `createRedux(stores)`: - -```js -const dispatcher = createDispatcher( - composeStores(stores), - getState => [thunkMiddleware(getState)] -); -``` - -`thunkMiddleware` is an example of a higher-order function that returns a middleware. - -After creating your dispatcher, pass it to `createRedux()`: - -```js -const redux = createRedux(dispatcher); -``` From 216437b705fbd075499db388a2960a09e4e49dd4 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 11:03:01 +0300 Subject: [PATCH 02/85] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 499951ebec..8798cc4874 100644 --- a/README.md +++ b/README.md @@ -17,4 +17,4 @@ Redux can be described in three fundamental principles: * **The only way to mutate the state is to emit an *action*, an object describing what happened.** This ensures that the views or the network callbacks never write directly to the state, and instead express the intent to mutate. Because all mutations are centralized and happen one by one in a strict order, there are no subtle race conditions to watch out for. Actions are just plain objects, so they can be logged, serialized, stored, and later replayed for debugging or testing purposes. -* **To specify how different parts of the state tree are transformed by the actions, you write pure *reducers*.** Reducers are just pure functions that take the previous state and the action, and return the next state. You can start with a single reducer, but as your app grows, you can split it into smaller reducers that manage the 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. +* **To specify how the state tree is transformed by the actions, you write pure *reducers*.** Reducers are just pure functions that take the previous state and the action, and return the next state. You can start with a single reducer, but as your app grows, you can split it into smaller reducers that manage the 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. From 6daf229f5d4f4eaa83653b4a891f78c95c58cb30 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 11:25:46 +0300 Subject: [PATCH 03/85] What do we need? --- docs/README.md | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 docs/README.md diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000000..722f796fce --- /dev/null +++ b/docs/README.md @@ -0,0 +1,52 @@ +### Redux Docs + +Work in progress ;-) + +What do we need here? +Maybe... + +#### Basics +- Three Principles +- Getting Started +- Comparison to Flux, Rx, etc +- Immutability +- API reference +- Glossary + +#### Anti patterns +- Many stores +- Impure reducers +- Unserializable actions + +#### Extension points +- Middleware +- Higher order stores +- Utilities + +#### Developer tools +- Hot reloading +- Redux DevTools with time travel + +#### How Do I... +- Stores + * waitFor + * read data from “another store” + +- Side effects + * subscribe to changes + * hook it up to Rx or something + +- AJAX + * Where to make calls + * How to await calls + * How to write reducers + * Middleware? + +- React + * React bindings + +- React Router + * How to transition after action is completed + +- Universal apps + * (Re)hydrating state \ No newline at end of file From 8604261a31ecce95d563352be7386ef34326ef8c Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 11:38:39 +0300 Subject: [PATCH 04/85] Update README.md --- docs/README.md | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/docs/README.md b/docs/README.md index 722f796fce..69c9063fac 100644 --- a/docs/README.md +++ b/docs/README.md @@ -6,28 +6,12 @@ What do we need here? Maybe... #### Basics -- Three Principles +- Key Ideas - Getting Started -- Comparison to Flux, Rx, etc -- Immutability -- API reference -- Glossary +- Comparison to Flux +- Understanding reducers -#### Anti patterns -- Many stores -- Impure reducers -- Unserializable actions - -#### Extension points -- Middleware -- Higher order stores -- Utilities - -#### Developer tools -- Hot reloading -- Redux DevTools with time travel - -#### How Do I... +#### Recipes - Stores * waitFor * read data from “another store” @@ -49,4 +33,22 @@ Maybe... * How to transition after action is completed - Universal apps - * (Re)hydrating state \ No newline at end of file + * (Re)hydrating state + +#### Anti-patterns +- Many stores +- Impure reducers +- Unserializable actions + +#### Advanced topics +- Middleware +- Higher order stores +- Performance + +#### Developer tools +- Hot reloading +- Redux DevTools with time travel + +#### Reference +- Full API reference +- Glossary From f0a1f5c66d9955a6d5cb2bf15769e34c293b7f8a Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 11:41:16 +0300 Subject: [PATCH 05/85] Update README.md --- docs/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/README.md b/docs/README.md index 69c9063fac..725518888a 100644 --- a/docs/README.md +++ b/docs/README.md @@ -9,7 +9,7 @@ Maybe... - Key Ideas - Getting Started - Comparison to Flux -- Understanding reducers +- Separation of concerns #### Recipes - Stores @@ -25,6 +25,7 @@ Maybe... * How to await calls * How to write reducers * Middleware? + * Authentication - React * React bindings @@ -43,7 +44,7 @@ Maybe... #### Advanced topics - Middleware - Higher order stores -- Performance +- Optimizing performance #### Developer tools - Hot reloading From 17f40095cf93d1f590e6c8d818719746936255aa Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 11:53:47 +0300 Subject: [PATCH 06/85] Update README.md --- docs/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index 725518888a..c559b09b03 100644 --- a/docs/README.md +++ b/docs/README.md @@ -8,7 +8,7 @@ Maybe... #### Basics - Key Ideas - Getting Started -- Comparison to Flux +- Comparison to Flux, Rx, etc - Separation of concerns #### Recipes @@ -36,6 +36,8 @@ Maybe... - Universal apps * (Re)hydrating state +- Testing + #### Anti-patterns - Many stores - Impure reducers From 90d65cb813804b800e1ad614ef7798f82d5df042 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 12:42:18 +0300 Subject: [PATCH 07/85] Update README.md --- docs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index c559b09b03..1ecf410bfc 100644 --- a/docs/README.md +++ b/docs/README.md @@ -9,7 +9,7 @@ Maybe... - Key Ideas - Getting Started - Comparison to Flux, Rx, etc -- Separation of concerns +- Separating concerns #### Recipes - Stores From 323d7b952ab38b80ae2e1c2a3a567aacb14b34f7 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 13:44:56 +0300 Subject: [PATCH 08/85] New docs directory structure --- docs/Basics/Core Ideas.md | 0 docs/Basics/Getting Started.md | 0 docs/Basics/Migrating to Redux.md | 0 docs/Basics/Relation to Other Libraries.md | 0 docs/Basics/The Redux Flow.md | 0 docs/Basics/Why Redux.md | 0 docs/README.md | 57 ------------------- docs/Recipes/Anti-Patterns.md | 0 docs/Recipes/Asynchronous Data Fetching.md | 0 docs/Recipes/Authentication.md | 0 docs/Recipes/Developer Tools.md | 0 docs/Recipes/Expressing Dependencies.md | 0 docs/Recipes/Higher Order Stores.md | 0 docs/Recipes/Hot Reloading.md | 0 docs/Recipes/Immutability.md | 0 docs/Recipes/Logging.md | 0 docs/Recipes/Middleware.md | 0 docs/Recipes/Optimizing Performance.md | 0 docs/Recipes/Pagination.md | 0 docs/Recipes/React Router.md | 0 docs/Recipes/React.md | 0 docs/Recipes/Recording and Replaying.md | 0 docs/Recipes/Relay and GraphQL.md | 0 docs/Recipes/Side Effects.md | 0 .../Thunks, Promises and Observables.md | 0 docs/Recipes/Unit Testing.md | 0 docs/Recipes/Universal Apps.md | 0 docs/Reference/API.md | 0 docs/Reference/Glossary.md | 0 29 files changed, 57 deletions(-) create mode 100644 docs/Basics/Core Ideas.md create mode 100644 docs/Basics/Getting Started.md create mode 100644 docs/Basics/Migrating to Redux.md create mode 100644 docs/Basics/Relation to Other Libraries.md create mode 100644 docs/Basics/The Redux Flow.md create mode 100644 docs/Basics/Why Redux.md delete mode 100644 docs/README.md create mode 100644 docs/Recipes/Anti-Patterns.md create mode 100644 docs/Recipes/Asynchronous Data Fetching.md create mode 100644 docs/Recipes/Authentication.md create mode 100644 docs/Recipes/Developer Tools.md create mode 100644 docs/Recipes/Expressing Dependencies.md create mode 100644 docs/Recipes/Higher Order Stores.md create mode 100644 docs/Recipes/Hot Reloading.md create mode 100644 docs/Recipes/Immutability.md create mode 100644 docs/Recipes/Logging.md create mode 100644 docs/Recipes/Middleware.md create mode 100644 docs/Recipes/Optimizing Performance.md create mode 100644 docs/Recipes/Pagination.md create mode 100644 docs/Recipes/React Router.md create mode 100644 docs/Recipes/React.md create mode 100644 docs/Recipes/Recording and Replaying.md create mode 100644 docs/Recipes/Relay and GraphQL.md create mode 100644 docs/Recipes/Side Effects.md create mode 100644 docs/Recipes/Thunks, Promises and Observables.md create mode 100644 docs/Recipes/Unit Testing.md create mode 100644 docs/Recipes/Universal Apps.md create mode 100644 docs/Reference/API.md create mode 100644 docs/Reference/Glossary.md diff --git a/docs/Basics/Core Ideas.md b/docs/Basics/Core Ideas.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/Basics/Getting Started.md b/docs/Basics/Getting Started.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/Basics/Migrating to Redux.md b/docs/Basics/Migrating to Redux.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/Basics/Relation to Other Libraries.md b/docs/Basics/Relation to Other Libraries.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/Basics/The Redux Flow.md b/docs/Basics/The Redux Flow.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/Basics/Why Redux.md b/docs/Basics/Why Redux.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index 1ecf410bfc..0000000000 --- a/docs/README.md +++ /dev/null @@ -1,57 +0,0 @@ -### Redux Docs - -Work in progress ;-) - -What do we need here? -Maybe... - -#### Basics -- Key Ideas -- Getting Started -- Comparison to Flux, Rx, etc -- Separating concerns - -#### Recipes -- Stores - * waitFor - * read data from “another store” - -- Side effects - * subscribe to changes - * hook it up to Rx or something - -- AJAX - * Where to make calls - * How to await calls - * How to write reducers - * Middleware? - * Authentication - -- React - * React bindings - -- React Router - * How to transition after action is completed - -- Universal apps - * (Re)hydrating state - -- Testing - -#### Anti-patterns -- Many stores -- Impure reducers -- Unserializable actions - -#### Advanced topics -- Middleware -- Higher order stores -- Optimizing performance - -#### Developer tools -- Hot reloading -- Redux DevTools with time travel - -#### Reference -- Full API reference -- Glossary diff --git a/docs/Recipes/Anti-Patterns.md b/docs/Recipes/Anti-Patterns.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/Recipes/Asynchronous Data Fetching.md b/docs/Recipes/Asynchronous Data Fetching.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/Recipes/Authentication.md b/docs/Recipes/Authentication.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/Recipes/Developer Tools.md b/docs/Recipes/Developer Tools.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/Recipes/Expressing Dependencies.md b/docs/Recipes/Expressing Dependencies.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/Recipes/Higher Order Stores.md b/docs/Recipes/Higher Order Stores.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/Recipes/Hot Reloading.md b/docs/Recipes/Hot Reloading.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/Recipes/Immutability.md b/docs/Recipes/Immutability.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/Recipes/Logging.md b/docs/Recipes/Logging.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/Recipes/Middleware.md b/docs/Recipes/Middleware.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/Recipes/Optimizing Performance.md b/docs/Recipes/Optimizing Performance.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/Recipes/Pagination.md b/docs/Recipes/Pagination.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/Recipes/React Router.md b/docs/Recipes/React Router.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/Recipes/React.md b/docs/Recipes/React.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/Recipes/Recording and Replaying.md b/docs/Recipes/Recording and Replaying.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/Recipes/Relay and GraphQL.md b/docs/Recipes/Relay and GraphQL.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/Recipes/Side Effects.md b/docs/Recipes/Side Effects.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/Recipes/Thunks, Promises and Observables.md b/docs/Recipes/Thunks, Promises and Observables.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/Recipes/Unit Testing.md b/docs/Recipes/Unit Testing.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/Recipes/Universal Apps.md b/docs/Recipes/Universal Apps.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/Reference/API.md b/docs/Reference/API.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/Reference/Glossary.md b/docs/Reference/Glossary.md new file mode 100644 index 0000000000..e69de29bb2 From c27f2742761451cd473dddff90d687732c0570c3 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 13:55:49 +0300 Subject: [PATCH 09/85] New docs directory structure --- docs/Basics/What Goes in Userland.md | 0 docs/Recipes/Code Splitting.md | 0 docs/Recipes/Derived Data.md | 0 docs/Resources/Articles.md | 0 docs/Resources/Examples.md | 0 docs/Resources/Getting Help.md | 0 docs/Resources/Utilities.md | 0 7 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/Basics/What Goes in Userland.md create mode 100644 docs/Recipes/Code Splitting.md create mode 100644 docs/Recipes/Derived Data.md create mode 100644 docs/Resources/Articles.md create mode 100644 docs/Resources/Examples.md create mode 100644 docs/Resources/Getting Help.md create mode 100644 docs/Resources/Utilities.md diff --git a/docs/Basics/What Goes in Userland.md b/docs/Basics/What Goes in Userland.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/Recipes/Code Splitting.md b/docs/Recipes/Code Splitting.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/Recipes/Derived Data.md b/docs/Recipes/Derived Data.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/Resources/Articles.md b/docs/Resources/Articles.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/Resources/Examples.md b/docs/Resources/Examples.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/Resources/Getting Help.md b/docs/Resources/Getting Help.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/Resources/Utilities.md b/docs/Resources/Utilities.md new file mode 100644 index 0000000000..e69de29bb2 From deb43bbadc1a87a81c258c8c1e1dde44b3b2b5ee Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 14:11:14 +0300 Subject: [PATCH 10/85] New docs directory structure --- docs/Recipes/React Native.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/Recipes/React Native.md diff --git a/docs/Recipes/React Native.md b/docs/Recipes/React Native.md new file mode 100644 index 0000000000..e69de29bb2 From 89b022ac4e4a52be3b8f164f91c0c9a7b2f8532a Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 14:13:01 +0300 Subject: [PATCH 11/85] Delete ROADMAP.md --- ROADMAP.md | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 ROADMAP.md diff --git a/ROADMAP.md b/ROADMAP.md deleted file mode 100644 index 7b644fb0d0..0000000000 --- a/ROADMAP.md +++ /dev/null @@ -1,21 +0,0 @@ -_**WIP**_ - -# Roadmap - -* Documentation Improvements - * Website - * Flow annotations - * Tutorial - * Glossary -* “Official” middleware and utilities - * Async - * `createStore`, `createActions`, etc -* Stateless dispatcher -* Action binding -* Powerful devtools - * Error recovery - * Time travel - * Logs -* Improve React Hot Loader - * Better error handling - * Support decorators From fe6c975d529ebe82d0bc66bc30d0a267a9b9e552 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 14:27:00 +0300 Subject: [PATCH 12/85] Update tagline --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6c4b443531..d79bf317a6 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "redux", "version": "1.0.0-rc", - "description": "Atomic Flux with hot reloading", + "description": "Predictable state management for JavaScript apps", "main": "lib/index.js", "scripts": { "clean": "rimraf lib dist coverage", From 74d5988ec4cf13fd4d00a7e9348255b6d62f4471 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 14:46:04 +0300 Subject: [PATCH 13/85] Update Core Ideas.md --- docs/Basics/Core Ideas.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/Basics/Core Ideas.md b/docs/Basics/Core Ideas.md index e69de29bb2..b2bfbff82d 100644 --- a/docs/Basics/Core Ideas.md +++ b/docs/Basics/Core Ideas.md @@ -0,0 +1,8 @@ + +Redux can be described in three fundamental principles: + +* **The whole state of your app is stored in an object tree inside a single *store*.** This makes it easy to create universal apps. The state from the server can be serialized and hydrated into the client with no extra coding effort. You can also persist your app’s state in development for a faster development cycle. And of course, with a single state tree, you get the previously difficult functionality like Undo/Redo for free. + +* **The only way to mutate the state is to emit an *action*, an object describing what happened.** This ensures that the views or the network callbacks never write directly to the state, and instead express the intent to mutate. Because all mutations are centralized and happen one by one in a strict order, there are no subtle race conditions to watch out for. Actions are just plain objects, so they can be logged, serialized, stored, and later replayed for debugging or testing purposes. + +* **To specify how the state tree is transformed by the actions, you write pure *reducers*.** Reducers are just pure functions that take the previous state and the action, and return the next state. You can start with a single reducer, but as your app grows, you can split it into smaller reducers that manage the 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. From e19bdecf8b4b8d13bae7dfe8eff42e089ac987f9 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 15:18:04 +0300 Subject: [PATCH 14/85] Doc structure --- docs/Recipes/Optimistic Updates.md | 0 docs/Recipes/Time Travel.md | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/Recipes/Optimistic Updates.md create mode 100644 docs/Recipes/Time Travel.md diff --git a/docs/Recipes/Optimistic Updates.md b/docs/Recipes/Optimistic Updates.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/Recipes/Time Travel.md b/docs/Recipes/Time Travel.md new file mode 100644 index 0000000000..e69de29bb2 From b1fdcd68559a2b58d63858e22793cf46220d0a0f Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 15:23:21 +0300 Subject: [PATCH 15/85] Update README.md --- README.md | 137 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 130 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 8798cc4874..1d41d4958c 100644 --- a/README.md +++ b/README.md @@ -5,16 +5,139 @@ redux [![npm version](https://img.shields.io/npm/v/redux.svg?style=flat-square)](https://www.npmjs.com/package/redux) [![redux channel on slack](https://img.shields.io/badge/slack-redux@reactiflux-61DAFB.svg?style=flat-square)](http://www.reactiflux.com) -Redux is an architecture for JavaScript applications with unidirectional data flow. It evolves the ideas of [Flux](https://facebook.github.io/flux), taking cues from [Elm](elm-lang.org/guide/architecture) and [Om](http://swannodette.github.io/2013/12/17/the-future-of-javascript-mvcs/) for a succinct approach based on functional composition. +Redux implements predictable state management for JavaScript apps. +It evolves the ideas of [Flux](https://facebook.github.io/flux), but avoids its complexity by taking cues from [Elm](elm-lang.org/guide/architecture). -Redux works both for client-side, universal, and native apps. You can use Redux together with [React](https://facebook.github.io/react/) or any other view library. Redux is tiny (2kB) and has no dependencies, but its ecosystem takes the developer experience to the next level. +Redux works both for client-side, universal, and native apps. +You can use Redux together with [React](https://facebook.github.io/react/) or any other view library. +Redux is tiny (2kB) and has no dependencies, but its ecosystem takes the developer experience to the next level. -## How It Works +## Installation -Redux can be described in three fundamental principles: +``` +npm install --save redux +``` -* **The whole state of your app is stored in an object tree inside a single *store*.** This makes it easy to create universal apps. The state from the server can be serialized and hydrated into the client with no extra coding effort. You can also persist your app’s state in development for a faster development cycle. And of course, with a single state tree, you get the previously difficult functionality like Undo/Redo for free. +You might also want to install the bindings for your view library of choice, for example: -* **The only way to mutate the state is to emit an *action*, an object describing what happened.** This ensures that the views or the network callbacks never write directly to the state, and instead express the intent to mutate. Because all mutations are centralized and happen one by one in a strict order, there are no subtle race conditions to watch out for. Actions are just plain objects, so they can be logged, serialized, stored, and later replayed for debugging or testing purposes. +``` +npm install --save react-redux +``` -* **To specify how the state tree is transformed by the actions, you write pure *reducers*.** Reducers are just pure functions that take the previous state and the action, and return the next state. You can start with a single reducer, but as your app grows, you can split it into smaller reducers that manage the 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. +## Documentation + +* [Basics](docs/Basics) +* [Recipes](docs/Recipes) +* [Reference](docs/Reference) +* [Resources](docs/Resources) + +## Testimonials + +>[“Love what you’re doing with Redux”](https://twitter.com/jingc/status/616608251463909376) +>Jing Chen, creator of Flux + +>[“I asked for comments on Redux in FB's internal JS discussion group, and it was universally praised. Really awesome work.”](https://twitter.com/fisherwebdev/status/616286955693682688) +>Bill Fisher, creator of Flux + +>[“It's cool that you are inventing a better Flux by not doing Flux at all.”](https://twitter.com/andrestaltz/status/616271392930201604) +>André Staltz, creator of Cycle + +## Developer Experience + +I wrote Redux while working on my React Europe talk called [“Hot Reloading with Time Travel”](https://www.youtube.com/watch?v=xsSnOQynTHs). My goal was to create a state management library with minimal API but completely predictable behavior, so it is possible to implement [logging](docs/Recipes/Logging.md), [hot reloading](docs/Recipes/Hot Reloading.md), [time travel](docs/Recipes/Time Travel.md), [universal apps](docs/Recipes/Universal Apps.md), [recording and replaying](docs/Recipes/Recording and Replaying.md), without any buy-in from the developer. + +## The Gist + +* The whole state of your app is stored in an object tree inside a single *store*. +* The only way to mutate the state is to emit an *action*, an object describing what happened. +* To specify how the state tree is transformed by the actions, you write pure *reducers*. +* [Learn more!](docs/Basics/Core Ideas.md) + +```js +import { createStore } from 'redux'; + +/** + * This is a reducer. + * It describes how an action transforms the state into the next state. + * + * In this example, we use a `switch` statement and strings, but you can use a helper that + * follows a different convention (such as function maps) that makes sense for your project. + * + * The only important part is (state, action) => state signature. + */ +function counter(state = 0, action) { + switch (action.type) { + case 'INCREMENT': + return state + 1; + case 'DECREMENT': + return state - 1; + default: + return 0; + } +} + +/** + * It turns out that reducers, being pure functions, are easy to compose. + * In fact, all state of your app can be described as a single reducer calling other reducers. + */ +function twoCounters(state = {}, action) { + switch (action.counterName) { + case 'first': + return { + first: counter(state.first, action), + second: state.second + }; + case 'second': + return { + first: state.first, + second: counter(state.second, action) + }; + default: + return { + first: counter(state.first, action), + second: counter(state.second, action) + }; + } +} + +// There is only a single store in a Redux app. +// It holds the complete state tree of your app. +let store = createStore(twoCounters); + +console.log(store.getState()); +// { first: 0, second: 0 } + +// You can subscribe to the updates manually, or using the bindings for your view library. +// It is possible to subscribe to updates of any granularity by comparing references. +// You can also use an efficient selector library that memoizes derived data for even better performance. + +store.subscribe(() => console.log(store.getState())); + +// The only way to mutate the internal state is to dispatch an action. +// The actions can be serialized, logged or stored and later replayed. + +store.dispatch({ type: 'INCREMENT' }); +// { first: 1, second: 1} + +store.dispatch({ type: 'INCREMENT', counterName: 'first' }); +// { first: 2, second: 1 } + +store.dispatch({ type: 'DECREMENT', counterName: 'second' }); +// { first: 2, second: 0 } + +// Pure functions are easy to test without mocking! +expect(twoCounters({ + first: 5, + second: 10 +}, { + type: 'INCREMENT', + counterName: 'first' +})).toEqual({ + first: 6, + second: 10 +}); +``` + +## License + +MIT From bb5a3327153aec3d2552d354d960a94cae6a4ac0 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 15:23:48 +0300 Subject: [PATCH 16/85] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1d41d4958c..3b95493e65 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ It evolves the ideas of [Flux](https://facebook.github.io/flux), but avoids its Redux works both for client-side, universal, and native apps. You can use Redux together with [React](https://facebook.github.io/react/) or any other view library. -Redux is tiny (2kB) and has no dependencies, but its ecosystem takes the developer experience to the next level. +It is tiny (2kB) and has no dependencies. ## Installation From 3d0b3ae5cf2c19f6f68cae74c547deb50a36a0bd Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 15:34:03 +0300 Subject: [PATCH 17/85] Update README.md --- README.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 3b95493e65..fe2489b558 100644 --- a/README.md +++ b/README.md @@ -48,22 +48,21 @@ I wrote Redux while working on my React Europe talk called [“Hot Reloading wit ## The Gist -* The whole state of your app is stored in an object tree inside a single *store*. -* The only way to mutate the state is to emit an *action*, an object describing what happened. -* To specify how the state tree is transformed by the actions, you write pure *reducers*. -* [Learn more!](docs/Basics/Core Ideas.md) +The whole state of your app is stored in an object tree inside a single *store*. +The only way to mutate the state is to emit an *action*, an object describing what happened. +To specify how the state tree is transformed by the actions, you write pure *reducers*. + +[Learn more!](docs/Basics/Core Ideas.md) ```js import { createStore } from 'redux'; /** - * This is a reducer. + * This is a reducer, a pure function with (state, action) => state signature. * It describes how an action transforms the state into the next state. * * In this example, we use a `switch` statement and strings, but you can use a helper that * follows a different convention (such as function maps) that makes sense for your project. - * - * The only important part is (state, action) => state signature. */ function counter(state = 0, action) { switch (action.type) { From cc17f4793408974e978f1ffcf0991e2a174d2152 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 16:03:31 +0300 Subject: [PATCH 18/85] Update README.md --- README.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/README.md b/README.md index fe2489b558..deb80c0bbe 100644 --- a/README.md +++ b/README.md @@ -102,25 +102,20 @@ function twoCounters(state = {}, action) { // There is only a single store in a Redux app. // It holds the complete state tree of your app. let store = createStore(twoCounters); - console.log(store.getState()); // { first: 0, second: 0 } // You can subscribe to the updates manually, or using the bindings for your view library. // It is possible to subscribe to updates of any granularity by comparing references. -// You can also use an efficient selector library that memoizes derived data for even better performance. - +// You can use an efficient selector library to memoize derived data for even better performance. store.subscribe(() => console.log(store.getState())); // The only way to mutate the internal state is to dispatch an action. // The actions can be serialized, logged or stored and later replayed. - store.dispatch({ type: 'INCREMENT' }); // { first: 1, second: 1} - store.dispatch({ type: 'INCREMENT', counterName: 'first' }); // { first: 2, second: 1 } - store.dispatch({ type: 'DECREMENT', counterName: 'second' }); // { first: 2, second: 0 } From 3045ffd20a90613f156935b11f68b14e63eb1446 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 16:28:13 +0300 Subject: [PATCH 19/85] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index deb80c0bbe..d8bb0ca934 100644 --- a/README.md +++ b/README.md @@ -105,9 +105,9 @@ let store = createStore(twoCounters); console.log(store.getState()); // { first: 0, second: 0 } -// You can subscribe to the updates manually, or using the bindings for your view library. +// You can subscribe to the updates manually, or use bindings to your view layer. // It is possible to subscribe to updates of any granularity by comparing references. -// You can use an efficient selector library to memoize derived data for even better performance. +// You can use a special library to compute and memoize derived data. store.subscribe(() => console.log(store.getState())); // The only way to mutate the internal state is to dispatch an action. From 96d3eeb87cc661e6a51c4f047bc725773f208b03 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 16:45:11 +0300 Subject: [PATCH 20/85] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d8bb0ca934..a3ed16ceb6 100644 --- a/README.md +++ b/README.md @@ -26,10 +26,10 @@ npm install --save react-redux ## Documentation -* [Basics](docs/Basics) -* [Recipes](docs/Recipes) -* [Reference](docs/Reference) -* [Resources](docs/Resources) +* [Basics](docs/Basics) cover the core ideas of Redux and its differences from other libraries. +* [Recipes](docs/Recipes) is a place to look for practical solutions to the problems you’ll encounter building an app. +* [Reference](docs/Reference) provides the glossary and the complete API documentation. +* [Resources](docs/Resources) is a curated list of the utilities, tools, and examples created by the community. ## Testimonials From 780a0b1faede47677b7244bd9e95c5efe90bf650 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 16:45:46 +0300 Subject: [PATCH 21/85] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a3ed16ceb6..a17691ce68 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ npm install --save react-redux ## Documentation * [Basics](docs/Basics) cover the core ideas of Redux and its differences from other libraries. -* [Recipes](docs/Recipes) is a place to look for practical solutions to the problems you’ll encounter building an app. +* [Recipes](docs/Recipes) are full of practical solutions to the problems you’ll encounter building an app. * [Reference](docs/Reference) provides the glossary and the complete API documentation. * [Resources](docs/Resources) is a curated list of the utilities, tools, and examples created by the community. From 982ce3a048a63221f461d2724c1ca73b94de17fc Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 17:15:30 +0300 Subject: [PATCH 22/85] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a17691ce68..3d30290837 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ redux Redux implements predictable state management for JavaScript apps. It evolves the ideas of [Flux](https://facebook.github.io/flux), but avoids its complexity by taking cues from [Elm](elm-lang.org/guide/architecture). -Redux works both for client-side, universal, and native apps. +Redux works for client-side, universal, and native apps. You can use Redux together with [React](https://facebook.github.io/react/) or any other view library. It is tiny (2kB) and has no dependencies. From 87b4b16f50563ae92939a928bde8da37d8c109d8 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 18:12:27 +0300 Subject: [PATCH 23/85] Doc structure --- docs/Recipes/Connecting UI.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/Recipes/Connecting UI.js diff --git a/docs/Recipes/Connecting UI.js b/docs/Recipes/Connecting UI.js new file mode 100644 index 0000000000..e69de29bb2 From eed718389b746cb2f66b9d6ffa9ccee5bad5b983 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 18:18:20 +0300 Subject: [PATCH 24/85] Update README.md --- README.md | 68 +++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 58 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 3d30290837..e2b2554751 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Redux works for client-side, universal, and native apps. You can use Redux together with [React](https://facebook.github.io/react/) or any other view library. It is tiny (2kB) and has no dependencies. -## Installation +### Installation ``` npm install --save redux @@ -24,14 +24,14 @@ You might also want to install the bindings for your view library of choice, for npm install --save react-redux ``` -## Documentation +### Documentation * [Basics](docs/Basics) cover the core ideas of Redux and its differences from other libraries. * [Recipes](docs/Recipes) are full of practical solutions to the problems you’ll encounter building an app. * [Reference](docs/Reference) provides the glossary and the complete API documentation. * [Resources](docs/Resources) is a curated list of the utilities, tools, and examples created by the community. -## Testimonials +### Testimonials >[“Love what you’re doing with Redux”](https://twitter.com/jingc/status/616608251463909376) >Jing Chen, creator of Flux @@ -42,11 +42,11 @@ npm install --save react-redux >[“It's cool that you are inventing a better Flux by not doing Flux at all.”](https://twitter.com/andrestaltz/status/616271392930201604) >André Staltz, creator of Cycle -## Developer Experience +### Developer Experience I wrote Redux while working on my React Europe talk called [“Hot Reloading with Time Travel”](https://www.youtube.com/watch?v=xsSnOQynTHs). My goal was to create a state management library with minimal API but completely predictable behavior, so it is possible to implement [logging](docs/Recipes/Logging.md), [hot reloading](docs/Recipes/Hot Reloading.md), [time travel](docs/Recipes/Time Travel.md), [universal apps](docs/Recipes/Universal Apps.md), [recording and replaying](docs/Recipes/Recording and Replaying.md), without any buy-in from the developer. -## The Gist +### The Gist The whole state of your app is stored in an object tree inside a single *store*. The only way to mutate the state is to emit an *action*, an object describing what happened. @@ -54,6 +54,10 @@ To specify how the state tree is transformed by the actions, you write pure *red [Learn more!](docs/Basics/Core Ideas.md) +#### A Counter + +Let’s start with an example where we increment and decrement a single counter. + ```js import { createStore } from 'redux'; @@ -75,9 +79,44 @@ function counter(state = 0, action) { } } +// Create a Redux store that holds the state of your app. +let store = createStore(counter); + +// You can read the current state of your store at any time. +console.log(store.getState()); +// 0 + +// You can subscribe to the updates manually, or use bindings to your view layer. +store.subscribe(() => + console.log(store.getState()) +); + +// The only way to mutate the internal state is to dispatch an action. +// The actions can be serialized, logged or stored and later replayed. +store.dispatch({ type: 'INCREMENT' }); +// 1 +store.dispatch({ type: 'INCREMENT' }); +// 2 +store.dispatch({ type: 'DECREMENT' }); +// 0 +``` + +Instead of mutating the state directly, you specify the mutations you want to happen with plain objects called *actions*. A special function called a *reducer* specifies how each action transforms the internal state. + +This might seem like an overkill for a counter app, but the beauty of this pattern is in 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 also record user sessions and reproduce them just by replaying every action. + +#### Two Counters + +Let’s say we now need to manage two counters separately. You don't need to throw away your `counter` function or create a second store! Remember, in Redux there is just a single store managing your whole application. + +Instead, we will create another function called `twoCounters` that *calls* your `counter` function and delegates its subtree to it. This is similar to how, in a React application, you would have a single root component that is described in terms of child components. Functional composition is a powerful tool! + +```js /** * It turns out that reducers, being pure functions, are easy to compose. * In fact, all state of your app can be described as a single reducer calling other reducers. + * We will delegate managing the counters to the `counter` function defined in the previous example. + * This function doesn't know *how* to update the counter—just that there are two counters! */ function twoCounters(state = {}, action) { switch (action.counterName) { @@ -99,16 +138,21 @@ function twoCounters(state = {}, action) { } } -// There is only a single store in a Redux app. +// Don’t forget there is only a single store in a Redux app. // It holds the complete state tree of your app. +// We have changed `createStore` call from the previous example to use `twoCounters` as the reducer. let store = createStore(twoCounters); + +// This time, the store’s state will contain the values of both counters! console.log(store.getState()); // { first: 0, second: 0 } -// You can subscribe to the updates manually, or use bindings to your view layer. +// You may subscribe to the updates manually, or use bindings to your view layer. // It is possible to subscribe to updates of any granularity by comparing references. // You can use a special library to compute and memoize derived data. -store.subscribe(() => console.log(store.getState())); +store.subscribe(() => { + console.log(store.getState()) +}); // The only way to mutate the internal state is to dispatch an action. // The actions can be serialized, logged or stored and later replayed. @@ -119,7 +163,7 @@ store.dispatch({ type: 'INCREMENT', counterName: 'first' }); store.dispatch({ type: 'DECREMENT', counterName: 'second' }); // { first: 2, second: 0 } -// Pure functions are easy to test without mocking! +// Bonus: pure functions are easy to test without mocking! expect(twoCounters({ first: 5, second: 10 @@ -132,6 +176,10 @@ expect(twoCounters({ }); ``` -## License +#### Next Steps + +You’ll probably want to connect Redux to the view layer of your choice. Check out [Getting Started](docs/Basics/Getting Started.md) for a more realistic app walkthrough with a suggest file structure, or head straight to [Connecting UI](docs/Recipes/Connecting UI.md) where you can find instructions on connecting the UI library of your choice to Redux. + +### License MIT From c54db712c79638863c9afaae3b721cb8e6679d14 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 18:20:36 +0300 Subject: [PATCH 25/85] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e2b2554751..966c62e6da 100644 --- a/README.md +++ b/README.md @@ -140,7 +140,7 @@ function twoCounters(state = {}, action) { // Don’t forget there is only a single store in a Redux app. // It holds the complete state tree of your app. -// We have changed `createStore` call from the previous example to use `twoCounters` as the reducer. +// We changed `createStore` call to use `twoCounters` as the reducer instead of `counter`. let store = createStore(twoCounters); // This time, the store’s state will contain the values of both counters! From 888f6e20f1fd9cc868c9fe06d9d2d6b9e22cf972 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 18:30:28 +0300 Subject: [PATCH 26/85] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 966c62e6da..af94442172 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,7 @@ function counter(state = 0, action) { case 'DECREMENT': return state - 1; default: - return 0; + return state; } } From a51b0f4eab5f90fa6d9066e5ea5251567a4de7b2 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 18:31:32 +0300 Subject: [PATCH 27/85] Doc structure --- docs/Recipes/Undo and Redo.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/Recipes/Undo and Redo.md diff --git a/docs/Recipes/Undo and Redo.md b/docs/Recipes/Undo and Redo.md new file mode 100644 index 0000000000..e69de29bb2 From f70d0667a3178b683b00eaecc6b3318abb11c456 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 18:36:18 +0300 Subject: [PATCH 28/85] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index af94442172..38cd9d0024 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,10 @@ import { createStore } from 'redux'; * 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 you should + * return a new object if the state changes, instead of mutating the parameter. + * * In this example, we use a `switch` statement and strings, but you can use a helper that * follows a different convention (such as function maps) that makes sense for your project. */ From 7ca6f140a0c9357b7b4b8fc3f86bb6a29e1ae539 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 19:05:11 +0300 Subject: [PATCH 29/85] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 38cd9d0024..040ff36cb7 100644 --- a/README.md +++ b/README.md @@ -182,7 +182,7 @@ expect(twoCounters({ #### Next Steps -You’ll probably want to connect Redux to the view layer of your choice. Check out [Getting Started](docs/Basics/Getting Started.md) for a more realistic app walkthrough with a suggest file structure, or head straight to [Connecting UI](docs/Recipes/Connecting UI.md) where you can find instructions on connecting the UI library of your choice to Redux. +You’ll probably want to connect Redux to the view layer of your choice. Check out [Getting Started](docs/Basics/Getting Started.md) for a more realistic app walkthrough with suggestions on how to structure your app, or head straight to [Connecting UI](docs/Recipes/Connecting UI.md) where you can read how to connect the UI library of your choice to Redux. ### License From bb1d19271ee69c95702d875db639b607ee2f10d3 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 19:06:17 +0300 Subject: [PATCH 30/85] lol --- docs/Recipes/{Connecting UI.js => Connecting UI.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/Recipes/{Connecting UI.js => Connecting UI.md} (100%) diff --git a/docs/Recipes/Connecting UI.js b/docs/Recipes/Connecting UI.md similarity index 100% rename from docs/Recipes/Connecting UI.js rename to docs/Recipes/Connecting UI.md From 4e01227da2fb4c329523c804321b28149d6a3c90 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 19:13:10 +0300 Subject: [PATCH 31/85] Update README.md --- README.md | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 040ff36cb7..aee1943c86 100644 --- a/README.md +++ b/README.md @@ -123,23 +123,13 @@ Instead, we will create another function called `twoCounters` that *calls* your * This function doesn't know *how* to update the counter—just that there are two counters! */ function twoCounters(state = {}, action) { - switch (action.counterName) { - case 'first': - return { - first: counter(state.first, action), - second: state.second - }; - case 'second': - return { - first: state.first, - second: counter(state.second, action) - }; - default: - return { - first: counter(state.first, action), - second: counter(state.second, action) - }; - } + var updateFirst = !action.counterName || action.counterName === 'first'; + var updateSecond = !action.counterName || action.counterName === 'second'; + + return { + first: updateFirst ? counter(state.first, action) : state.first, + second: updateSecond ? counter(state.second, action) : state.second + }; } // Don’t forget there is only a single store in a Redux app. From 942bcfe8b1dae52c8db50eae8dbf5ece579477a8 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 19:16:52 +0300 Subject: [PATCH 32/85] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index aee1943c86..e325d38003 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Redux implements predictable state management for JavaScript apps. It evolves the ideas of [Flux](https://facebook.github.io/flux), but avoids its complexity by taking cues from [Elm](elm-lang.org/guide/architecture). Redux works for client-side, universal, and native apps. -You can use Redux together with [React](https://facebook.github.io/react/) or any other view library. +You can use Redux together with [React](https://facebook.github.io/react/), as well as any other view library. It is tiny (2kB) and has no dependencies. ### Installation From c7f1315c3fd504b00c90064436e882dc50a4f692 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 19:17:26 +0300 Subject: [PATCH 33/85] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e325d38003..57f48549aa 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Redux implements predictable state management for JavaScript apps. It evolves the ideas of [Flux](https://facebook.github.io/flux), but avoids its complexity by taking cues from [Elm](elm-lang.org/guide/architecture). Redux works for client-side, universal, and native apps. -You can use Redux together with [React](https://facebook.github.io/react/), as well as any other view library. +You can use Redux together with [React](https://facebook.github.io/react/), or with any other view library. It is tiny (2kB) and has no dependencies. ### Installation From f9c026404c280c7fb80213ba044efec074125cf5 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 19:20:16 +0300 Subject: [PATCH 34/85] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 57f48549aa..78f7abf8fe 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ store.dispatch({ type: 'INCREMENT' }); store.dispatch({ type: 'INCREMENT' }); // 2 store.dispatch({ type: 'DECREMENT' }); -// 0 +// 1 ``` Instead of mutating the state directly, you specify the mutations you want to happen with plain objects called *actions*. A special function called a *reducer* specifies how each action transforms the internal state. From d7bfb6e88859aea529e8325bdf01f5288f10821a Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 19:23:10 +0300 Subject: [PATCH 35/85] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 78f7abf8fe..f4903f143b 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,7 @@ store.dispatch({ type: 'DECREMENT' }); Instead of mutating the state directly, you specify the mutations you want to happen with plain objects called *actions*. A special function called a *reducer* specifies how each action transforms the internal state. -This might seem like an overkill for a counter app, but the beauty of this pattern is in 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 also record user sessions and reproduce them just by replaying every action. +This 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 also record user sessions and reproduce them just by replaying every action. #### Two Counters From 9332f0021a85e6d6a4e80ec9a3506bad33ccedf7 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 19:24:51 +0300 Subject: [PATCH 36/85] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f4903f143b..ceb70e658b 100644 --- a/README.md +++ b/README.md @@ -111,7 +111,7 @@ This might seem like an overkill for a counter app, but the beauty of this patte #### Two Counters -Let’s say we now need to manage two counters separately. You don't need to throw away your `counter` function or create a second store! Remember, in Redux there is just a single store managing your whole application. +Let’s say we now need to manage two counters separately, and we want to support updating either one or both counters with a single method call. You don't need to throw away your `counter` function or create a second store! Remember, in Redux there is just a single store managing your whole application. Instead, we will create another function called `twoCounters` that *calls* your `counter` function and delegates its subtree to it. This is similar to how, in a React application, you would have a single root component that is described in terms of child components. Functional composition is a powerful tool! From c8de1acb0e8d886046c965b75d54b0c1d878fd48 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 19:54:07 +0300 Subject: [PATCH 37/85] Update README.md --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index ceb70e658b..f79c629ce2 100644 --- a/README.md +++ b/README.md @@ -119,8 +119,7 @@ Instead, we will create another function called `twoCounters` that *calls* your /** * It turns out that reducers, being pure functions, are easy to compose. * In fact, all state of your app can be described as a single reducer calling other reducers. - * We will delegate managing the counters to the `counter` function defined in the previous example. - * This function doesn't know *how* to update the counter—just that there are two counters! + * We will delegate managing the counters to the `counter` function from the previous example. */ function twoCounters(state = {}, action) { var updateFirst = !action.counterName || action.counterName === 'first'; From 608c18cc3fce20d4b683f156ca1e3e8a75c98f43 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 21:31:25 +0300 Subject: [PATCH 38/85] Update Core Ideas.md --- docs/Basics/Core Ideas.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/Basics/Core Ideas.md b/docs/Basics/Core Ideas.md index b2bfbff82d..5d6c6e41f5 100644 --- a/docs/Basics/Core Ideas.md +++ b/docs/Basics/Core Ideas.md @@ -1,3 +1,5 @@ +Core Ideas +-------------------------- Redux can be described in three fundamental principles: From a8048875ce8b9b3ded1e89e05d31d7622afbbfa1 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 21:35:32 +0300 Subject: [PATCH 39/85] Update Core Ideas.md --- docs/Basics/Core Ideas.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/Basics/Core Ideas.md b/docs/Basics/Core Ideas.md index 5d6c6e41f5..a61431acd5 100644 --- a/docs/Basics/Core Ideas.md +++ b/docs/Basics/Core Ideas.md @@ -8,3 +8,7 @@ Redux can be described in three fundamental principles: * **The only way to mutate the state is to emit an *action*, an object describing what happened.** This ensures that the views or the network callbacks never write directly to the state, and instead express the intent to mutate. Because all mutations are centralized and happen one by one in a strict order, there are no subtle race conditions to watch out for. Actions are just plain objects, so they can be logged, serialized, stored, and later replayed for debugging or testing purposes. * **To specify how the state tree is transformed by the actions, you write pure *reducers*.** Reducers are just pure functions that take the previous state and the action, and return the next state. You can start with a single reducer, but as your app grows, you can split it into smaller reducers that manage the 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. + +-------------------------- + +Next: [Getting Started](Getting Started.md) From 63dfe6e601dccb3e3d073e2575b28d8eaa9fa030 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 21:37:36 +0300 Subject: [PATCH 40/85] Update Getting Started.md --- docs/Basics/Getting Started.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/Basics/Getting Started.md b/docs/Basics/Getting Started.md index e69de29bb2..06d8c3418f 100644 --- a/docs/Basics/Getting Started.md +++ b/docs/Basics/Getting Started.md @@ -0,0 +1,8 @@ +Getting Started +-------------------------- + +TODO + +-------------------------- + +Next: [Migrating to Redux](Migrating to Redux.md) From b01316584f87c93a7e131af7b7acedacfeecb61b Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 21:38:35 +0300 Subject: [PATCH 41/85] Update Migrating to Redux.md --- docs/Basics/Migrating to Redux.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/Basics/Migrating to Redux.md b/docs/Basics/Migrating to Redux.md index e69de29bb2..fd4209cb83 100644 --- a/docs/Basics/Migrating to Redux.md +++ b/docs/Basics/Migrating to Redux.md @@ -0,0 +1,7 @@ +Migrating to Redux +-------------------------- + +TODO + +-------------------------- +Next: [Relation to Other Libraries](Relation to Other Libraries.md) From 5cde95d4b8e374ea16fe78a609ced9a799e10b89 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 21:39:22 +0300 Subject: [PATCH 42/85] Update Relation to Other Libraries.md --- docs/Basics/Relation to Other Libraries.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/Basics/Relation to Other Libraries.md b/docs/Basics/Relation to Other Libraries.md index e69de29bb2..2b535f63a9 100644 --- a/docs/Basics/Relation to Other Libraries.md +++ b/docs/Basics/Relation to Other Libraries.md @@ -0,0 +1,7 @@ +Relation to Other Libraries +-------------------------- + +TODO + +-------------------------- +Next: [The Redux Flow](The Redux Flow.md) From 644c48fad434dd8333f3e109e50d6a4367def01f Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 21:39:53 +0300 Subject: [PATCH 43/85] Rename What Goes in Userland.md to Userland and Core.md --- docs/Basics/Userland and Core.md | 1 + docs/Basics/What Goes in Userland.md | 0 2 files changed, 1 insertion(+) create mode 100644 docs/Basics/Userland and Core.md delete mode 100644 docs/Basics/What Goes in Userland.md diff --git a/docs/Basics/Userland and Core.md b/docs/Basics/Userland and Core.md new file mode 100644 index 0000000000..d3f5a12faa --- /dev/null +++ b/docs/Basics/Userland and Core.md @@ -0,0 +1 @@ + diff --git a/docs/Basics/What Goes in Userland.md b/docs/Basics/What Goes in Userland.md deleted file mode 100644 index e69de29bb2..0000000000 From 260fb948313bc417bcbaca9778ad283e6cfed3b8 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 21:40:15 +0300 Subject: [PATCH 44/85] Update The Redux Flow.md --- docs/Basics/The Redux Flow.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/Basics/The Redux Flow.md b/docs/Basics/The Redux Flow.md index e69de29bb2..030bad1119 100644 --- a/docs/Basics/The Redux Flow.md +++ b/docs/Basics/The Redux Flow.md @@ -0,0 +1,7 @@ +The Redux Flow +-------------------------- + +TODO + +-------------------------- +Next: [Userland and Core](Userland and Core.md) From cf432dce0aa99c5436dfd4aaf98e1e3b44b90315 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 21:40:43 +0300 Subject: [PATCH 45/85] Update Userland and Core.md --- docs/Basics/Userland and Core.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/Basics/Userland and Core.md b/docs/Basics/Userland and Core.md index d3f5a12faa..1a0cd7f973 100644 --- a/docs/Basics/Userland and Core.md +++ b/docs/Basics/Userland and Core.md @@ -1 +1,7 @@ +Userland and Core +-------------------------- +TODO + +-------------------------- +Next: [Why Redux](Why Redux.md) From 50880cf4f868923e9a86242c9cd6581bf5a6f983 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 21:45:59 +0300 Subject: [PATCH 46/85] Update Why Redux.md --- docs/Basics/Why Redux.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/Basics/Why Redux.md b/docs/Basics/Why Redux.md index e69de29bb2..4330baca01 100644 --- a/docs/Basics/Why Redux.md +++ b/docs/Basics/Why Redux.md @@ -0,0 +1,9 @@ +Why Redux +-------------------------- + +TODO + +-------------------------- + +Done with the basics? +Check out [the practical recipes](../Recipes), [the glossary and the full API reference](../Reference), and [the community resources](../Resources). From 7b0473d9eeec203b6373524a5aa7029989b3bd34 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 23:35:00 +0300 Subject: [PATCH 47/85] Update Glossary.md --- docs/Reference/Glossary.md | 122 +++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) diff --git a/docs/Reference/Glossary.md b/docs/Reference/Glossary.md index e69de29bb2..16c204e6d5 100644 --- a/docs/Reference/Glossary.md +++ b/docs/Reference/Glossary.md @@ -0,0 +1,122 @@ +Glossary +===================== + +This is a glossary of the core terms in Redux, along with their type signatures. +Types are documented using [Flow notation](http://flowtype.org/docs/quick-reference.html#_). + +### State + +```js +type State = any; +``` + +**State** 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. + +### Action + +```js +type Action = Object; +``` + +An **action** is a plain object that represents an intention to change the state. The only way to change the state inside a store is by dispatching an action. By convention, actions contain a string `type` property which indicates the nature of the action being dispatched. It is advisable that your actions are serializable, so that you can [record and replay](../Recipes/Recording and Replaying.md) user sessions. This is why we recommend to describe action types as string constants instead of Symbols. + +See also **intermediate actions** below. + +### Dispatching function + +```js +type Dispatch = (a: Action | IntermediateAction) => any; +``` + +A **dispatching function** (or simply **dispatch function**) is a function that accepts an action or an intermediate 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 the 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. + +A middleware wraps the base dispatch function, potentially transforming, delaying, ignoring, or otherwise interpreting the intermediate actions. See below for more information. + +### Reducer + +```js +type Reducer = (state: S, action: A) => S; +``` + +A **reducer** or **reducing function** is a function that accepts an accumulation and a value and returns a new accumulation. They are used to reduce a collection of values down to a single value. Reducers are not unique to Redux—they are a fundamental concept in functional programming. Even most non-functional languages, like JavaScript, have a built-in API for reducing. (In JavaScript, it's [`Array.prototype.reduce()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce).) + +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 functions*—functions 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](../Recipes/Hot Reloading.md) and [time travel](../Recipes/Time Travel.md). + +Reducers are the most important concept in Redux. + +### Action creator + +```js +type ActionCreator = (...args: any) => Action | IntermediateAction; +``` + +An **action creator** is, quite simply, a function that creates an action. Do not confuse the two terms—again, an action is a payload of information, and an action creator is a factory that creates them. + +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. + +### Intermediate action + +```js +type IntermediateAction = any +``` + +An **intermediate 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 before being sent to the base `dispatch()` function. Intermediate actions are often asynchronous primitives, like a promise or a thunk, which are not dispatched themselves, but trigger dispatches once an operation has completed. + +### Middleware + +```js +type MiddlewareAPI = { dispatch: Dispatch, getState: () => State }; +type Middleware = (api: MiddlewareAPI) => (next: Dispatch) => Dispatch; +``` + +A middleware is a higher-order function that composes a dispatch function to return a new dispatch function. + +* The outermost function receives an object of methods which are a subset of the Store interface: `dispatch()` and `getState()`. This gives the inner function access to these methods. +* That returns another function, which receives a dispatch function. This dispatch function is not necessarily the same as the base dispatch function passed to the outermost function — it is the next dispatch function in the middleware chain. +* The innermost function is a dispatch function. It receives an action, and can either call the next dispatch function in the chain, or call the base dispatch function to restart the chain. It can call either function asynchronously and multiple times, or it can call nothing at all. A no-op middleware should synchronously call `next(action)`. + +Middleware is composable using function composition. It is useful for [logging actions](../Recipes/Logging.md), performing side effects like [routing](../Recipes/React Router.md), or turning an [asynchronous API call](https://github.com/gaearon/redux/blob/rewrite-docs/docs/Recipes/Asynchronous%20Data%20Fetching.md) into a series of synchronous actions. + +### Store + +```js +type Store = { + dispatch: Dispatch; + getState: () => State; + subscribe: (listener: () => void) => () => void; + getReducer: () => Reducer; + replaceReducer: (reducer: Reducer) => void +}; +``` + +A store is an object that holds the application’s state tree. +There should only be a single store in a Redux app, as the composition happens on the reducer level. + +- `dispatch()` is the base dispatch function described above. +- `getState()` returns the current state of the store. +- `subscribe()` registers a function to be called on state changes. It returns an unsubscribe function. +- `getReducer()` and `replaceReducer()` can be used to implement hot reloading and code splitting. Most likely you won’t use them. + +### Store creator + +```js +type StoreCreator = (reducer: Reducer, initialState: any) => Store +``` + +A store creator is a function that creates a Redux store. Like with dispatching function, we must distinguish the base store creator, `createStore()` exported from the Redux package, from store creators that are returned from the store enhancers. + +### Store enhancer + +```js +type StoreEnhancer = (next: StoreCreator) => StoreCreator +``` + +A store enhancer is a higher-order function that composes a store creator to return a new, enhanced store creator. This is similar to middleware in that it allows you to alter the store interface in a composable way. + +Store enhancers are much the same concept as higher-order components in React, which are also occasionally called “component enhancers”. + +Because a store is not an instance, but rather a plain-object collection of functions, copies can be easily created and modified without mutating the original store. + +Most likely you’ll never write a store enhancer, but you may use the one provided by the [developer tools](../Recipes/Developer Tools.md). It is what makes [time travel](../Recipes/Time Travel.md) possible without the app being aware it is happening. From d0b86f149e58ede924e52315e1ce5d49cfe8b457 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 23 Jul 2015 23:38:46 +0300 Subject: [PATCH 48/85] Update Glossary.md --- docs/Reference/Glossary.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/Reference/Glossary.md b/docs/Reference/Glossary.md index 16c204e6d5..553ddd25ff 100644 --- a/docs/Reference/Glossary.md +++ b/docs/Reference/Glossary.md @@ -59,7 +59,7 @@ Calling an action creator only produces an action, but does not dispatch it. You ### Intermediate action ```js -type IntermediateAction = any +type IntermediateAction = any; ``` An **intermediate 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 before being sent to the base `dispatch()` function. Intermediate actions are often asynchronous primitives, like a promise or a thunk, which are not dispatched themselves, but trigger dispatches once an operation has completed. @@ -87,7 +87,7 @@ type Store = { getState: () => State; subscribe: (listener: () => void) => () => void; getReducer: () => Reducer; - replaceReducer: (reducer: Reducer) => void + replaceReducer: (reducer: Reducer) => void; }; ``` @@ -102,7 +102,7 @@ There should only be a single store in a Redux app, as the composition happens o ### Store creator ```js -type StoreCreator = (reducer: Reducer, initialState: any) => Store +type StoreCreator = (reducer: Reducer, initialState: any) => Store; ``` A store creator is a function that creates a Redux store. Like with dispatching function, we must distinguish the base store creator, `createStore()` exported from the Redux package, from store creators that are returned from the store enhancers. @@ -110,7 +110,7 @@ A store creator is a function that creates a Redux store. Like with dispatching ### Store enhancer ```js -type StoreEnhancer = (next: StoreCreator) => StoreCreator +type StoreEnhancer = (next: StoreCreator) => StoreCreator; ``` A store enhancer is a higher-order function that composes a store creator to return a new, enhanced store creator. This is similar to middleware in that it allows you to alter the store interface in a composable way. From 6d58b003f6d35ab3d6175d742a670335371f76fe Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Fri, 24 Jul 2015 19:17:12 +0300 Subject: [PATCH 49/85] Update API.md --- docs/Reference/API.md | 184 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+) diff --git a/docs/Reference/API.md b/docs/Reference/API.md index e69de29bb2..ba521e2820 100644 --- a/docs/Reference/API.md +++ b/docs/Reference/API.md @@ -0,0 +1,184 @@ +API +===================== + +Redux API surface is tiny. Instead of being a monolithic framework, Redux defines a set of contracts for you to implement (such as [reducers](./Glossary.md#reducer)) and provides a few helper functions to tie these contracts together. + +This file documents the complete Redux API. Keep in mind that Redux is only concerned with managing the state. In a real app, you’ll want to use [UI bindings](../Recipes/Connecting UI.md) to the view library of your choice, such as [React](../Recipes/React.md). + +Every function described below is a top-level export. You can access them like this: + +```js +// ES6 +import { createStore } from 'redux'; + +// ES5 (CommonJS) +var createStore = require('redux').createStore; + +// ES5 (UMD build) +var createStore = Redux.createStore; +``` + +===================== + +### `createStore(reducer: Reducer, initialState: any): Store` + +Creates and returns a Redux store that holds the state tree. +The only way to change the data in the store is to call `dispatch()` on it. + +There should only be a single store in your app. + +##### Parameters + +* `reducer`: Required. A [reducer](./Glossary.md#reducer) function that returns the next state tree, given +the current state tree and the action to handle. + +* `initialState: any`: The initial [state](./Glossary.md#state). You may optionally specify it +to hydrate the state from the server in universal apps, or to restore a +previously serialized user session. + +##### Example + +```js +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'] +``` + +##### Notes + +**There should only be a single store in your app.** To specify how different +parts of the state tree respond to actions, you may combine several reducers +into a single reducer function by using `combineReducers`. + +In [universal apps](../Recipes/Universal Apps.md), you’ll want to create a store +instance per request so that they are isolated, and dispatch a few actions +to fetch the data before rendering the app. + +===================== + +### Store API + +Once you obtain a Redux [store](../Glossary.md#store) from `createStore` to manage your state, +you may call these methods on it. + +#### `getState(): any` + +Returns the current state tree of your application. + +#### `dispatch(action: Action | IntermediateAction): any` + +Dispatches an action. It is the only way to trigger a state change. + +The `reducer` function the store was created with will be called with the +current state tree and the and the given `action`. Its return value will +be considered the next state of the tree, and the change listeners will be +notified. + +The base implementation only supports plain object actions. However, if you wrapped +`createStore` with `applyMiddleware`, the [middleware](./Glossary.md#middleware) can interpret actions differently, +and provide support asynchronous primitives like promises, observables, thunks or something else +that makes sense for your project. + +##### Example (no middleware) + +```js +import { createStore } from 'redux'; +let store = createStore(todos, ['Use Redux']); + +function addTodo(text) { + return { type: 'ADD_TODO', text }; +} + +// No middleware: you can only dispatch plain object actions +store.dispatch(addTodo('Read the docs')); +store.dispatch(addTodo('Read about the middleware')); +``` + +##### Example ([redux-thunk](https://github.com/gaearon/redux-thunk) middleware) + +```js +import { createStore, applyMiddleware } from 'redux'; +import thunk from 'redux-thunk'; + +let createStoreWithMiddleware = applyMiddleware(thunk)(createStore); +let store = createStoreWithMiddleware(todos, ['Use Redux']); + +function addTodo(text) { + return { type: 'ADD_TODO', text }; +} + +function addTodoAsync(text, delay) { + // redux-thunk knows how to handle functions + return function (dispatch) { + setTimeout(function () { + dispatch(addTodo(text)); + }, delay); + }; +} + +store.dispatch(addTodo('Read the docs')); +store.dispatch(addTodoAsync('Learn to use middleware', 1000)); +``` + +#### `subscribe(listener: Function): Function` + +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. + +Any [bindings for a view layer](../Recipes/Connecting UI.md) internally use `subscribe` +to be notified of the state changes. If you want to do something sophisticated with +the stream of resulting values, it might be a good idea to use a library like [Rx](https://github.com/Reactive-Extensions/RxJS) +and turn the store subscription into a proper Observable. + +Returns a function to remove this change listener. + +##### Example + +```js +function select(state) { + return state.some.deep.property; +} + +let currentValue; +function handleChange() { + let previousValue = currentValue; + currentValue = select(store.getState()); + + if (previousValue !== currentValue) { + console.log('Some deep nested property changed from', previousValue, 'to', currentValue); + } +} + +let unsubscribe = store.subscribe(handleChange); +handleChange(); +``` + +#### `getReducer(): Reducer` + +Returns the reducer currently used by the store to calculate the state. + +It is likely that you will only need this function if you implement a [hot +reloading](../Recipes/Hot Reloading.md) mechanism for Redux. + +#### `replaceReducer(nextReducer: Reducer): void` + +Replaces the reducer currently used by the store to calculate the state. + +You might need this if your app implements [code splitting](../Recipes/Code Splitting.md) and you want to +load some of the reducers dynamically. You might also need this if you +implement a hot reloading mechanism for Redux. + +===================== + From 40149cdf16d3e391261b5a4c6a38fa554a683c70 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Fri, 24 Jul 2015 19:26:22 +0300 Subject: [PATCH 50/85] Update API.md --- docs/Reference/API.md | 47 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/docs/Reference/API.md b/docs/Reference/API.md index ba521e2820..1f39b1e44e 100644 --- a/docs/Reference/API.md +++ b/docs/Reference/API.md @@ -29,7 +29,7 @@ There should only be a single store in your app. ##### Parameters -* `reducer`: Required. A [reducer](./Glossary.md#reducer) function that returns the next state tree, given +* `reducer: Reducer`: Required. A [reducer](./Glossary.md#reducer) function that returns the next state tree, given the current state tree and the action to handle. * `initialState: any`: The initial [state](./Glossary.md#state). You may optionally specify it @@ -90,6 +90,9 @@ The base implementation only supports plain object actions. However, if you wrap and provide support asynchronous primitives like promises, observables, thunks or something else that makes sense for your project. +By default, it returns the action you just dispatched, but the middleware can override the return result. +For example, an async middleware may return a promise so you can wait for the dispatch completion. + ##### Example (no middleware) ```js @@ -182,3 +185,45 @@ implement a hot reloading mechanism for Redux. ===================== +### `combineReducers(reducers: Object): Reducer` + +Turns an object whose values are different reducer functions, into a single +reducer function. It will call every child reducer, and gather their results +into a single state object, whose keys correspond to the keys of the passed +reducer functions. + +Returns a reducer function that invokes every reducer inside the +passed object, and builds a state object with the same shape. + +##### Parameters + +* `reducers: Object`: An object whose values correspond to different +reducer functions that need to be combined into one. One handy way to obtain +it is to use ES6 `import * as reducers` syntax. The reducers may never return +`undefined` for any action. Instead, they should return their initial state +if the state passed to them was undefined, and the current state for any +unrecognized action. + +##### Example + +```js +// reducers.js +export function todos(state = [], action) { + // ... +} +export function counter(state = 0, action) { + // ... +} + +// App.js +import { createStore, combineReducers } from 'redux'; + +import * as reducers from './reducers'; +console.log(reducers); +// { todos: Function, counter: Function } + +let reducer = combineReducers(reducers); +let store = createStore(reducer); +console.log(store.getState()); +// { counter: 0, todos: [] } +``` From 470bb06f1314cf2b9e113cda65480c70cc07175a Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Fri, 24 Jul 2015 19:49:07 +0300 Subject: [PATCH 51/85] Update API.md --- docs/Reference/API.md | 98 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/docs/Reference/API.md b/docs/Reference/API.md index 1f39b1e44e..f50606533f 100644 --- a/docs/Reference/API.md +++ b/docs/Reference/API.md @@ -227,3 +227,101 @@ let store = createStore(reducer); console.log(store.getState()); // { counter: 0, todos: [] } ``` + +===================== + +### `applyMiddleware` + +Creates a [store enhancer](./Glossary.md#store-enhancer) that applies middleware to the dispatch method +of the Redux store. This is handy for a variety of tasks, such as expressing +asynchronous actions in a concise manner, or logging every action payload. + +Because middleware is potentially asynchronous, this should be the first store enhancer in the composition chain. + +Returns a store enhancer function that needs to be applied to `createStore` to add any middleware to it. + +##### Parameters + +* `...middlewares: Array`: The middleware chain to be applied. + +##### Example + +```js +function logger({ getState }) { + return (next) => (action) => { + console.log('will dispatch', action); + let result = next(action); + console.log('state after dispatch', getState()); + return result; + }; +} + +import { createStore, applyMiddleware } from 'redux'; +import thunk from 'redux-thunk'; + +let createStoreWithMiddleware = applyMiddleware(thunk, logger)(createStore); +let store = createStoreWithMiddleware(todos, ['Use Redux']); +``` + +===================== + +### `bindActionCreators(actionCreators: Object, dispatch: Function): Object` + +Turns an object whose values are [action creators](./Glossary.md#action-creators), +into an object with the same keys, but with every function wrapped into a `dispatch` call +so they may be invoked directly. This is just a convenience method, as you can call +`store.dispatch(MyActionCreators.doSomething())` yourself just fine. + +Returns an object mimicking the original object, but with every +action creator wrapped into the `dispatch` call. + +##### Parameters + +* `actionCreators: Object`: An object whose values are action creator +functions. One handy way to obtain it is to use ES6 `import * as` syntax. + +* `dispatch: Function`: The `dispatch` function available on your Redux +store. + +##### Example + +```js +// actionCreators.js +function addTodo(text) { + // ... +} +function removeTodo(id) { + // ... +} + + +// SomeComponent.js +import * as actionCreators from './actionCreators'; +console.log(actionCreators); +// { addTodo: Function, removeTodo: function } + +// You can always dispatch yourself: +store.dispatch(actionCreators.addTodo('Use Redux')); + +// But it can be handy to bind action creators to a store instance: +let boundActionCreators = bindActionCreators(action, store.dispatch); +boundActionCreators.addTodo('Use Redux'); +// You can pass them down and decouple components below from the Redux store. +``` + +===================== + +### `compose(...funcs)` + +Composes functions from left to right. + +This is a common function programming utility, and is included in Redux as convenience. +You might want to use it to apply several store enhancers in a row. + +Returns a function that passes its only argument to the first of +the `funcs`, then pipes its return value to the second one, and so on, until +the last of the `funcs` is called, and its result is returned. + +#### Parameters + +* `...funcs: Array`: The functions to compose. From 4e64d478aee556deb0ec94a32ac842434d796c91 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Fri, 24 Jul 2015 19:52:07 +0300 Subject: [PATCH 52/85] Update API.md --- docs/Reference/API.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Reference/API.md b/docs/Reference/API.md index f50606533f..5e61183ae0 100644 --- a/docs/Reference/API.md +++ b/docs/Reference/API.md @@ -230,7 +230,7 @@ console.log(store.getState()); ===================== -### `applyMiddleware` +### `applyMiddleware(...middlewares: Array): StoreEnhancer` Creates a [store enhancer](./Glossary.md#store-enhancer) that applies middleware to the dispatch method of the Redux store. This is handy for a variety of tasks, such as expressing @@ -311,7 +311,7 @@ boundActionCreators.addTodo('Use Redux'); ===================== -### `compose(...funcs)` +### `compose(...funcs: Array): Function` Composes functions from left to right. From 81b88fbb2c85122df4f5bcc9f6c3a4c67140913e Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Fri, 24 Jul 2015 19:54:16 +0300 Subject: [PATCH 53/85] Update API.md --- docs/Reference/API.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Reference/API.md b/docs/Reference/API.md index 5e61183ae0..8732298869 100644 --- a/docs/Reference/API.md +++ b/docs/Reference/API.md @@ -87,7 +87,7 @@ notified. The base implementation only supports plain object actions. However, if you wrapped `createStore` with `applyMiddleware`, the [middleware](./Glossary.md#middleware) can interpret actions differently, -and provide support asynchronous primitives like promises, observables, thunks or something else +and provide support for asynchronous primitives like promises, observables, thunks or something else that makes sense for your project. By default, it returns the action you just dispatched, but the middleware can override the return result. From 18d7717b428907b45815112179c4c9bebf94f4ce Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Fri, 24 Jul 2015 19:59:50 +0300 Subject: [PATCH 54/85] Higher-order store => Store enhancer --- src/utils/applyMiddleware.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/utils/applyMiddleware.js b/src/utils/applyMiddleware.js index f3852d1d51..734f23e5df 100644 --- a/src/utils/applyMiddleware.js +++ b/src/utils/applyMiddleware.js @@ -1,17 +1,17 @@ import compose from './compose'; /** - * Creates a higher-order store that applies middleware to the dispatch method + * Creates a store enhancer that applies middleware to the dispatch method * of the Redux store. This is handy for a variety of tasks, such as expressing * asynchronous actions in a concise manner, or logging every action payload. * * See `redux-thunk` package as an example of the Redux middleware. * * Because middleware is potentially asynchronous, this should be the first - * higher-order store in the composition chain. + * store enhancer in the composition chain. * * @param {...Function} middlewares The middleware chain to be applied. - * @returns {Function} A higher-order store. + * @returns {Function} A store enhancer applying the middleware. */ export default function applyMiddleware(...middlewares) { return (next) => (reducer, initialState) => { From 49443da9be4bd60613d8e5c6fcb44bb162407d3b Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Fri, 24 Jul 2015 20:30:28 +0300 Subject: [PATCH 55/85] Update API.md --- docs/Reference/API.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/Reference/API.md b/docs/Reference/API.md index 8732298869..e958b4ed45 100644 --- a/docs/Reference/API.md +++ b/docs/Reference/API.md @@ -1,6 +1,8 @@ API ===================== +>**All type signatures and terms in this document are mentioned in the [Glossary](./Glossary.md).** + Redux API surface is tiny. Instead of being a monolithic framework, Redux defines a set of contracts for you to implement (such as [reducers](./Glossary.md#reducer)) and provides a few helper functions to tie these contracts together. This file documents the complete Redux API. Keep in mind that Redux is only concerned with managing the state. In a real app, you’ll want to use [UI bindings](../Recipes/Connecting UI.md) to the view library of your choice, such as [React](../Recipes/React.md). From 33265b3a3fe1481afe5c4a81224048078d0f6367 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Fri, 24 Jul 2015 20:52:07 +0300 Subject: [PATCH 56/85] Rename Higher Order Stores.md to Stores Enhancers.md --- docs/Recipes/Higher Order Stores.md | 0 docs/Recipes/Stores Enhancers.md | 1 + 2 files changed, 1 insertion(+) delete mode 100644 docs/Recipes/Higher Order Stores.md create mode 100644 docs/Recipes/Stores Enhancers.md diff --git a/docs/Recipes/Higher Order Stores.md b/docs/Recipes/Higher Order Stores.md deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/docs/Recipes/Stores Enhancers.md b/docs/Recipes/Stores Enhancers.md new file mode 100644 index 0000000000..d3f5a12faa --- /dev/null +++ b/docs/Recipes/Stores Enhancers.md @@ -0,0 +1 @@ + From 2fca51d6e0b83eafb39c4d07ef1fda7561a8c7b1 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Fri, 24 Jul 2015 21:53:58 +0300 Subject: [PATCH 57/85] Update Migrating to Redux.md --- docs/Basics/Migrating to Redux.md | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/docs/Basics/Migrating to Redux.md b/docs/Basics/Migrating to Redux.md index fd4209cb83..70caf33512 100644 --- a/docs/Basics/Migrating to Redux.md +++ b/docs/Basics/Migrating to Redux.md @@ -1,7 +1,27 @@ Migrating to Redux -------------------------- -TODO +#### From Flux Libraries + +[Reducer functions](../Reference/Glossary.md#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](http://github.com/acdlite/flummox), [Alt](http://github.com/goatslacker/alt), [vanilla Flux](https://github.com/facebook/flux), or any other Flux library. + +Your process will look like this: + +* Create a function called `createFluxStore(reducer)` that creates a Flux store compatible with your existing app from a reducer function. Internally it might look similar to [`createStore`](../../src/createStore.js) implementation from Redux. Its dispatch handler should just call the `reducer` for any action, store the next state, and emit change. + +* 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. + +* 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 as you port it to be based on reducers! + +* When you have ported all of your Flux Stores to be implement with reducers, you can replace the Flux library with a single Redux store, and combine those reducers you already have into one using `combineReducers(reducers)`. + +* Now all that’s left to do is to port the UI to use Redux bindings for your view library instead of the ones that came with your Flux library, and your app uses Redux! + +* Finally, you might want to begin using some Redux idioms like middleware to further simplify your asynchronous code. + +#### From Backbone + +Sorry, you’ll need to rewrite your model layer :-(. -------------------------- Next: [Relation to Other Libraries](Relation to Other Libraries.md) From 80a0cf85c82a7e28419051cb789c2299c420b8f9 Mon Sep 17 00:00:00 2001 From: ellbee Date: Fri, 24 Jul 2015 22:26:23 +0100 Subject: [PATCH 58/85] Add TOC to API.md --- docs/Reference/API.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/Reference/API.md b/docs/Reference/API.md index e958b4ed45..abcb3f8604 100644 --- a/docs/Reference/API.md +++ b/docs/Reference/API.md @@ -20,6 +20,20 @@ var createStore = require('redux').createStore; var createStore = Redux.createStore; ``` +#### Table of Contents + +* [createStore(reducer: Reducer, initialState: any): Store](#createstorereducer-reducer-initialstate-any-store) +* [Store API](#store-api) + * [getState(): any](#getstate-any) + * [dispatch(action: Action | IntermediateAction): any](#dispatchaction-action--intermediateaction-any) + * [subscribe(listener: Function): Function](#subscribelistener-function-function) + * [getReducer(): Reducer](#getreducer-reducer) + * [replaceReducer(nextReducer: Reducer): void](#replacereducernextreducer-reducer-void) +* [combineReducers(reducers: Object): Reducer](#combinereducersreducers-object-reducer) +* [applyMiddleware(...middlewares: Array): StoreEnhancer](#applymiddlewaremiddlewares-arraymiddleware-storeenhancer) +* [bindActionCreators(actionCreators: Object, dispatch: Function): Object](#bindactioncreatorsactioncreators-object-dispatch-function-object) +* [compose(...funcs: Array): Function](#composefuncs-arrayfunction-function) + ===================== ### `createStore(reducer: Reducer, initialState: any): Store` From 5c2ce3fd09befdeec5950cff81332965a50a08f5 Mon Sep 17 00:00:00 2001 From: Misha Moroshko Date: Sat, 25 Jul 2015 14:27:57 +1000 Subject: [PATCH 59/85] Add missing paren --- docs/Reference/API.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Reference/API.md b/docs/Reference/API.md index abcb3f8604..33d52665f4 100644 --- a/docs/Reference/API.md +++ b/docs/Reference/API.md @@ -65,7 +65,7 @@ function todos(state = [], action) { } let store = createStore(todos, ['Use Redux']); -store.dispatch({ type: 'ADD_TODO', text: 'Read the docs'); +store.dispatch({ type: 'ADD_TODO', text: 'Read the docs' }); console.log(store.getState()); // ['Use Redux', 'Read the docs'] From da33002d8ba046751e1085ddeea70694b6f74965 Mon Sep 17 00:00:00 2001 From: Misha Moroshko Date: Sat, 25 Jul 2015 14:36:08 +1000 Subject: [PATCH 60/85] Remove duplicate text --- docs/Reference/API.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Reference/API.md b/docs/Reference/API.md index abcb3f8604..ca720bfe3f 100644 --- a/docs/Reference/API.md +++ b/docs/Reference/API.md @@ -97,7 +97,7 @@ Returns the current state tree of your application. Dispatches an action. It is the only way to trigger a state change. The `reducer` function the store was created with will be called with the -current state tree and the and the given `action`. Its return value will +current state tree and the given `action`. Its return value will be considered the next state of the tree, and the change listeners will be notified. From e5e073b39a9e9c7cee969d32659f697ec073644c Mon Sep 17 00:00:00 2001 From: Misha Moroshko Date: Sat, 25 Jul 2015 15:13:19 +1000 Subject: [PATCH 61/85] Fix "action creators" link --- docs/Reference/API.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Reference/API.md b/docs/Reference/API.md index abcb3f8604..770611778d 100644 --- a/docs/Reference/API.md +++ b/docs/Reference/API.md @@ -283,7 +283,7 @@ let store = createStoreWithMiddleware(todos, ['Use Redux']); ### `bindActionCreators(actionCreators: Object, dispatch: Function): Object` -Turns an object whose values are [action creators](./Glossary.md#action-creators), +Turns an object whose values are [action creators](./Glossary.md#action-creator), into an object with the same keys, but with every function wrapped into a `dispatch` call so they may be invoked directly. This is just a convenience method, as you can call `store.dispatch(MyActionCreators.doSomething())` yourself just fine. From 808c32d4be413568838761a8cb812b501c3bfbee Mon Sep 17 00:00:00 2001 From: Misha Moroshko Date: Sat, 25 Jul 2015 15:22:34 +1000 Subject: [PATCH 62/85] Fix `bindActionCreators` example --- docs/Reference/API.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/Reference/API.md b/docs/Reference/API.md index abcb3f8604..05ef3363d1 100644 --- a/docs/Reference/API.md +++ b/docs/Reference/API.md @@ -303,10 +303,10 @@ store. ```js // actionCreators.js -function addTodo(text) { +export function addTodo(text) { // ... } -function removeTodo(id) { +export function removeTodo(id) { // ... } @@ -320,7 +320,7 @@ console.log(actionCreators); store.dispatch(actionCreators.addTodo('Use Redux')); // But it can be handy to bind action creators to a store instance: -let boundActionCreators = bindActionCreators(action, store.dispatch); +let boundActionCreators = bindActionCreators(actionCreators, store.dispatch); boundActionCreators.addTodo('Use Redux'); // You can pass them down and decouple components below from the Redux store. ``` From 42a9e581832db8571479e4bd7133470dafea8ca6 Mon Sep 17 00:00:00 2001 From: Misha Moroshko Date: Sat, 25 Jul 2015 15:25:23 +1000 Subject: [PATCH 63/85] Change `` to `` for consistency --- docs/Reference/API.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Reference/API.md b/docs/Reference/API.md index abcb3f8604..d23ebfc598 100644 --- a/docs/Reference/API.md +++ b/docs/Reference/API.md @@ -340,4 +340,4 @@ the last of the `funcs` is called, and its result is returned. #### Parameters -* `...funcs: Array`: The functions to compose. +* `...funcs: Array`: The functions to compose. From 6d6df19f120d554cc783fc842570265b78ef9d5e Mon Sep 17 00:00:00 2001 From: Nicola Molinari Date: Sat, 25 Jul 2015 22:39:32 +0200 Subject: [PATCH 64/85] JSDoc improvements for createStore --- src/createStore.js | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/createStore.js b/src/createStore.js index 2cbfd1b18b..009348f227 100644 --- a/src/createStore.js +++ b/src/createStore.js @@ -22,9 +22,10 @@ export var ActionTypes = { * @param {Function} reducer A function that returns the next state tree, given * the current state tree and the action to handle. * - * @param {any} initialState The initial state. You may optionally specify it + * @param {Object} [initialState] 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. + * previously serialized user session. The object keys must match the reducer names + * managed by `combineReducers`. * * @returns {Store} A Redux store that lets you read the state, dispatch actions * and subscribe to changes. @@ -42,7 +43,7 @@ export default function createStore(reducer, initialState) { /** * Reads the state tree managed by the store. * - * @returns {any} The current state tree of your application. + * @returns {Object} The current state tree of your application. */ function getState() { return currentState; @@ -68,22 +69,23 @@ export default function createStore(reducer, initialState) { /** * Dispatches an action. It is the only way to trigger a state change. * - * The `reducer` function the store was created with will be called with the - * current state tree and the and the given `action`. Its return value will - * be considered the next state of the tree, and the change listeners will be - * notified. + * The `reducer` function, used to create the store, will be called with the + * current state tree and the given `action`. Its return value will + * be considered the **next** state of the tree, and the change listeners + * will be notified. * * The base implementation only supports plain object actions. If you want to - * dispatch a promise, an observable, a thunk, or something else, you need to + * dispatch a Promise, an Observable, a thunk, or something else, you need to * wrap your store creating function into the corresponding middleware. For * example, see the documentation for the `redux-thunk` package. Even the * middleware will eventually dispatch plain object actions using this method. * * @param {Object} action A plain object representing “what changed”. It is * a good idea to keep actions serializable so you can record and replay user - * sessions, or use the time travelling Redux developer tools. + * sessions, or use the time travelling `redux-devtools`. * * @returns {Object} For convenience, the same action object you dispatched. + * * Note that, if you use a custom middleware, it may wrap `dispatch()` to * return something else (for example, a Promise you can await). */ @@ -125,6 +127,11 @@ export default function createStore(reducer, initialState) { dispatch({ type: ActionTypes.INIT }); } + /** + * When a store is created, an "INIT" action is dispatched so that every + * reducer will return their initial state. This effectively populates + * the initial store tree. + */ dispatch({ type: ActionTypes.INIT }); return { From e2f6bafd32bf5295103a1213cfcf109baad70b7f Mon Sep 17 00:00:00 2001 From: Nicola Molinari Date: Sat, 25 Jul 2015 22:42:46 +0200 Subject: [PATCH 65/85] Revert "JSDoc improvements for createStore" This reverts commit 6d6df19f120d554cc783fc842570265b78ef9d5e. --- src/createStore.js | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/src/createStore.js b/src/createStore.js index 009348f227..2cbfd1b18b 100644 --- a/src/createStore.js +++ b/src/createStore.js @@ -22,10 +22,9 @@ export var ActionTypes = { * @param {Function} reducer A function that returns the next state tree, given * the current state tree and the action to handle. * - * @param {Object} [initialState] The initial state. You may optionally specify it + * @param {any} initialState 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. The object keys must match the reducer names - * managed by `combineReducers`. + * previously serialized user session. * * @returns {Store} A Redux store that lets you read the state, dispatch actions * and subscribe to changes. @@ -43,7 +42,7 @@ export default function createStore(reducer, initialState) { /** * Reads the state tree managed by the store. * - * @returns {Object} The current state tree of your application. + * @returns {any} The current state tree of your application. */ function getState() { return currentState; @@ -69,23 +68,22 @@ export default function createStore(reducer, initialState) { /** * Dispatches an action. It is the only way to trigger a state change. * - * The `reducer` function, used to create the store, will be called with the - * current state tree and the given `action`. Its return value will - * be considered the **next** state of the tree, and the change listeners - * will be notified. + * The `reducer` function the store was created with will be called with the + * current state tree and the and the given `action`. Its return value will + * be considered the next state of the tree, and the change listeners will be + * notified. * * The base implementation only supports plain object actions. If you want to - * dispatch a Promise, an Observable, a thunk, or something else, you need to + * dispatch a promise, an observable, a thunk, or something else, you need to * wrap your store creating function into the corresponding middleware. For * example, see the documentation for the `redux-thunk` package. Even the * middleware will eventually dispatch plain object actions using this method. * * @param {Object} action A plain object representing “what changed”. It is * a good idea to keep actions serializable so you can record and replay user - * sessions, or use the time travelling `redux-devtools`. + * sessions, or use the time travelling Redux developer tools. * * @returns {Object} For convenience, the same action object you dispatched. - * * Note that, if you use a custom middleware, it may wrap `dispatch()` to * return something else (for example, a Promise you can await). */ @@ -127,11 +125,6 @@ export default function createStore(reducer, initialState) { dispatch({ type: ActionTypes.INIT }); } - /** - * When a store is created, an "INIT" action is dispatched so that every - * reducer will return their initial state. This effectively populates - * the initial store tree. - */ dispatch({ type: ActionTypes.INIT }); return { From c74a877c6a369778bac3a1659da3cc1e5d58e10a Mon Sep 17 00:00:00 2001 From: Eugene Rodionov Date: Mon, 27 Jul 2015 14:33:02 +0300 Subject: [PATCH 66/85] feat: add gitbook (#311) --- SUMMARY.md | 45 +++++++++++++++++++++++++++++++++++++++++++++ package.json | 6 ++++-- 2 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 SUMMARY.md diff --git a/SUMMARY.md b/SUMMARY.md new file mode 100644 index 0000000000..281089a5a4 --- /dev/null +++ b/SUMMARY.md @@ -0,0 +1,45 @@ +# Summary +* Basics + * [Core Ideas](docs/Basics/Core Ideas.md) + * [Getting Started](docs/Basics/Getting Started.md) + * [Migrating to Redux](docs/Basics/Migrating to Redux.md) + * [Relation to Other Libraries](docs/Basics/Relation to Other Libraries.md) + * [The Redux Flow](docs/Basics/The Redux Flow.md) + * [Userland and Core](docs/Basics/Userland and Core.md) + * [Why Redux](docs/Basics/Why Redux.md) +* Recipes + * [Anti-Patterns](docs/Recipes/Anti-Patterns.md) + * [Asynchronous Data Fetching](docs/Recipes/Asynchronous Data Fetching.md) + * [Authentication](docs/Recipes/Authentication.md) + * [Code Splitting](docs/Recipes/Code Splitting.md) + * [Connecting UI](docs/Recipes/Connecting UI.md) + * [Derived Data](docs/Recipes/Derived Data.md) + * [Developer Tools](docs/Recipes/Developer Tools.md) + * [Expressing Dependencies](docs/Recipes/Expressing Dependencies.md) + * [Hot Reloading](docs/Recipes/Hot Reloading.md) + * [Immutability](docs/Recipes/Immutability.md) + * [Logging](docs/Recipes/Logging.md) + * [Middleware](docs/Recipes/Middleware.md) + * [Optimistic Updates](docs/Recipes/Optimistic Updates.md) + * [Optimizing Performance](docs/Recipes/Optimizing Performance.md) + * [Pagination](docs/Recipes/Pagination.md) + * [React Native](docs/Recipes/React Native.md) + * [React Router](docs/Recipes/React Router.md) + * [React](docs/Recipes/React.md) + * [Recording and Replaying](docs/Recipes/Recording and Replaying.md) + * [Relay and GraphQL](docs/Recipes/Relay and GraphQL.md) + * [Side Effects](docs/Recipes/Side Effects.md) + * [Stores Enhancers](docs/Recipes/Stores Enhancers.md) + * [Thunks, Promises and Observables](docs/Recipes/Thunks, Promises and Observables.md) + * [Time Travel](docs/Recipes/Time Travel.md) + * [Undo and Redo](docs/Recipes/Undo and Redo.md) + * [Unit Testing](docs/Recipes/Unit Testing.md) + * [Universal Apps](docs/Recipes/Universal Apps.md) +* Reference + * [API](docs/Reference/API.md) + * [Glossary](docs/Reference/Glossary.md) +* Resources + * [Articles](docs/Resources/Articles.md) + * [Examples](docs/Resources/Examples.md) + * [Getting Help](docs/Resources/Getting Help.md) + * [Utilities](docs/Resources/Utilities.md) diff --git a/package.json b/package.json index d79bf317a6..189952898b 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "lint": "eslint src test examples", "test": "NODE_ENV=test mocha --compilers js:babel/register --recursive", "test:watch": "npm test -- --watch", - "test:cov": "babel-node ./node_modules/.bin/isparta cover ./node_modules/.bin/_mocha -- --recursive", + "test:cov": "babel-node $(npm bin)/isparta cover $(npm bin)/_mocha -- --recursive", "check": "npm run lint && npm run test", "build:lib": "babel src --out-dir lib", "build:umd": "webpack src/index.js dist/redux.js && NODE_ENV=production webpack src/index.js dist/redux.min.js", @@ -16,7 +16,8 @@ "preversion": "npm run clean && npm run check", "version": "npm run build", "postversion": "git push && git push --tags && npm run clean", - "prepublish": "npm run clean && npm run build" + "prepublish": "npm run clean && npm run build", + "docs:build": "$(npm bin)/gitbook build" }, "repository": { "type": "git", @@ -50,6 +51,7 @@ "eslint-config-airbnb": "0.0.6", "eslint-plugin-react": "^2.3.0", "expect": "^1.6.0", + "gitbook-cli": "^0.3.4", "isparta": "^3.0.3", "mocha": "^2.2.5", "rimraf": "^2.3.4", From ecfd1276a6d680a55c6848766735af396d606381 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Mon, 27 Jul 2015 16:17:51 +0300 Subject: [PATCH 67/85] Update Relation to Other Libraries.md --- docs/Basics/Relation to Other Libraries.md | 39 +++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/docs/Basics/Relation to Other Libraries.md b/docs/Basics/Relation to Other Libraries.md index 2b535f63a9..5181b9fb5a 100644 --- a/docs/Basics/Relation to Other Libraries.md +++ b/docs/Basics/Relation to Other Libraries.md @@ -1,7 +1,44 @@ Relation to Other Libraries -------------------------- -TODO +### Flux + +Can Redux be considered a [Flux](https://facebook.github.io/flux/) implementation? +[Yes](https://twitter.com/fisherwebdev/status/616278911886884864), and [no](https://twitter.com/andrestaltz/status/616270755605708800). + +(Don’t worry, [Flux creators](https://twitter.com/jingc/status/616608251463909376) [approve of it](https://twitter.com/fisherwebdev/status/616286955693682688), if that’s all you wanted to know.) + +Redux was inspired by several important qualities of Flux. Like Flux, Redux prescribes you to 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 “action”. + +Unlike Flux, **Redux does not have a concept of 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. WDepending on how you view Flux, you may see this as a deviation or an implementation detail. Flux has often been [described as `(state, action) => state`](https://speakerdeck.com/jmorrell/jsconf-uy-flux-those-who-forget-the-past-dot-dot-dot). 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 severely discouraged. You should always return a new object, which is easy with the [object spread syntax proposed for ES7](https://github.com/sebmarkbage/ecmascript-rest-spread) and implemented in [Babel](http://babeljs.io), or with a library like [Immutable](https://facebook.github.io/immutable-js). + +While it is technically *possible* to [write impure reducers](https://github.com/gaearon/redux/issues/328#issuecomment-125035516) 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 of the real apps, because, as [Om](https://github.com/omcljs/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. + +### Elm + +[Elm](http://elm-lang.org/) is a functional programming language created by [Evan Czaplicki](https://twitter.com/czaplic). It encourages using [an architecture that can be described as `(state, action) => state`](http://elm-lang.org/guide/architecture). Technically, Elm “updaters” are equivalent to the reducers in Redux. + +Unlike Redux, Elm is a language, so it is able to benefit from statical typing for actions, and pattern matching. 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](https://github.com/paldepind/noname-functional-frontend-framework). We should look there for inspiration on Redux! One way how we can get closer to the static typing of Elm is by [using a gradual typing solution like Flow](https://github.com/gaearon/redux/issues/290). + +### Immutable + +[Immutable](https://facebook.github.io/immutable-js) 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 state—it 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 if your immutable library supports cursors, you should not use them in a Redux app. The whole tree should be considered read-only, and you should use Redux for updating the state, and subscribing to the updates. **If you’re happy with cursors, you don’t need Redux.** + +### Baobab + +[Baobab](https://github.com/Yomguithereal/baobab) is another popular library implementing immutable API for updating plain JavaScript objects. While you can use it with Redux, there is little benefit to 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 the 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. -------------------------- Next: [The Redux Flow](The Redux Flow.md) From ac9319d5e9330bd494857f280a4c64cb51af09dd Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Mon, 27 Jul 2015 16:19:32 +0300 Subject: [PATCH 68/85] Update Relation to Other Libraries.md --- docs/Basics/Relation to Other Libraries.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Basics/Relation to Other Libraries.md b/docs/Basics/Relation to Other Libraries.md index 5181b9fb5a..37790b544f 100644 --- a/docs/Basics/Relation to Other Libraries.md +++ b/docs/Basics/Relation to Other Libraries.md @@ -10,7 +10,7 @@ Can Redux be considered a [Flux](https://facebook.github.io/flux/) implementatio Redux was inspired by several important qualities of Flux. Like Flux, Redux prescribes you to 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 “action”. -Unlike Flux, **Redux does not have a concept of 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. WDepending on how you view Flux, you may see this as a deviation or an implementation detail. Flux has often been [described as `(state, action) => state`](https://speakerdeck.com/jmorrell/jsconf-uy-flux-those-who-forget-the-past-dot-dot-dot). In this sense, Redux is true to the Flux architecture, but makes it simpler thanks to pure functions. +Unlike Flux, **Redux does not have a concept of 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 a deviation or an implementation detail. Flux has often been [described as `(state, action) => state`](https://speakerdeck.com/jmorrell/jsconf-uy-flux-those-who-forget-the-past-dot-dot-dot). 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 severely discouraged. You should always return a new object, which is easy with the [object spread syntax proposed for ES7](https://github.com/sebmarkbage/ecmascript-rest-spread) and implemented in [Babel](http://babeljs.io), or with a library like [Immutable](https://facebook.github.io/immutable-js). From 1096c55ae670303ad6bbcf238e13fee495569e9e Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Mon, 27 Jul 2015 16:35:52 +0300 Subject: [PATCH 69/85] Update Relation to Other Libraries.md --- docs/Basics/Relation to Other Libraries.md | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/docs/Basics/Relation to Other Libraries.md b/docs/Basics/Relation to Other Libraries.md index 37790b544f..ca7f2cd46d 100644 --- a/docs/Basics/Relation to Other Libraries.md +++ b/docs/Basics/Relation to Other Libraries.md @@ -40,5 +40,29 @@ Most of the functionality Baobab provides is related to updating the data with c 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](https://github.com/Reactive-Extensions/RxJS) (and their undergoing [modern rewrite](https://github.com/ReactiveX/RxJS)) 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](http://cycle.js.org). + +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: + +```js +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](https://github.com/jas-chen/rx-redux). 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](github.com/gaearon/redux-devtools)). 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](http://cycle.js.org) instead, or even combine it with Redux. Let us know how it goes! + -------------------------- Next: [The Redux Flow](The Redux Flow.md) From 08199a0abe7410c9e7bd2104d7742900a4ab628d Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Mon, 27 Jul 2015 17:00:10 +0300 Subject: [PATCH 70/85] Update Relation to Other Libraries.md --- docs/Basics/Relation to Other Libraries.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Basics/Relation to Other Libraries.md b/docs/Basics/Relation to Other Libraries.md index ca7f2cd46d..d0d98be6b7 100644 --- a/docs/Basics/Relation to Other Libraries.md +++ b/docs/Basics/Relation to Other Libraries.md @@ -30,7 +30,7 @@ Immutable and most similar libraries are orthogonal to Redux. Feel free to use t **Redux doesn’t care *how* you store the state—it 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 if your immutable library supports cursors, you should not use them in a Redux app. The whole tree should be considered read-only, and you should use Redux for updating the state, and subscribing to the updates. **If you’re happy with cursors, you don’t need Redux.** +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](http://github.com/faassen/reselect) for a really great and concise implementation of composable selectors. ### Baobab From 66a0fb7f9b3aa40e1f1d9b8d21a656d735785380 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Mon, 27 Jul 2015 17:17:11 +0300 Subject: [PATCH 71/85] Update Why Redux.md --- docs/Basics/Why Redux.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/Basics/Why Redux.md b/docs/Basics/Why Redux.md index 4330baca01..5e8c055cbc 100644 --- a/docs/Basics/Why Redux.md +++ b/docs/Basics/Why Redux.md @@ -1,7 +1,16 @@ Why Redux -------------------------- -TODO +Redux was an experiment in taking Flux concepts and tweaking them until [hot reloading, record and replay, and time travel as demoed in Dan’s talk](http://youtube.com/watch?v=xsSnOQynTHs) are easy to implement in userland. Redux proved to be successful in its own right, but it’s important to understand its roots and initial focus. + +There are a few key design decisions behind Redux that are “set in stone” and won’t change. Whether you agree with them or not, it’s good to understand them before choosing in favor of Redux or against it. + +* The only way to mutate the state is by dispatching a plain object called “action”. +* It should be possible to implement the [developer tools with hot reloading and time travel](http://github.com/gaearon/redux-devtools) outside the core. +* For universal apps and developer tools, it should be possible to serialize all state to an object and later hydrate it. +* We should play nice with libraries like Rx, and Immutable, but not enforce using them. +* The API surface should be minimal. Most of your code should be portable to the “next thing” whatever it is. +* It should be easy to test all parts of your code without mocking. -------------------------- From df5b9ce27400888bc20bfd9d07ec463698f050b1 Mon Sep 17 00:00:00 2001 From: Nicola Molinari Date: Sat, 25 Jul 2015 22:44:20 +0200 Subject: [PATCH 72/85] JSDoc improvements JSDoc improvements for applyMiddleware Update API.md with improvements of createStore doc JSdoc tweaks Adjustments based on review Typos Correctly define jsdoc param args Typo --- docs/Reference/API.md | 4 +++- src/createStore.js | 21 ++++++++++++++------- src/utils/applyMiddleware.js | 3 +++ 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/docs/Reference/API.md b/docs/Reference/API.md index 31e0656c39..5993b7684a 100644 --- a/docs/Reference/API.md +++ b/docs/Reference/API.md @@ -50,7 +50,7 @@ the current state tree and the action to handle. * `initialState: any`: The initial [state](./Glossary.md#state). You may optionally specify it to hydrate the state from the server in universal apps, or to restore a -previously serialized user session. +previously serialized user session. If you use `combineReducers` to produce the root reducer function, this must be an object with the same shape as `combineReducers` keys. ##### Example @@ -256,6 +256,8 @@ Because middleware is potentially asynchronous, this should be the first store e Returns a store enhancer function that needs to be applied to `createStore` to add any middleware to it. +Note that each middleware will be given the `dispatch` and `getState` functions as named arguments. + ##### Parameters * `...middlewares: Array`: The middleware chain to be applied. diff --git a/src/createStore.js b/src/createStore.js index 2cbfd1b18b..22c0d7dd0d 100644 --- a/src/createStore.js +++ b/src/createStore.js @@ -22,9 +22,11 @@ export var ActionTypes = { * @param {Function} reducer A function that returns the next state tree, given * the current state tree and the action to handle. * - * @param {any} initialState The initial state. You may optionally specify it + * @param {any} [initialState] 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 use `combineReducers` to produce the root reducer function, this must be + * an object with the same shape as `combineReducers` keys. * * @returns {Store} A Redux store that lets you read the state, dispatch actions * and subscribe to changes. @@ -68,22 +70,23 @@ export default function createStore(reducer, initialState) { /** * Dispatches an action. It is the only way to trigger a state change. * - * The `reducer` function the store was created with will be called with the - * current state tree and the and the given `action`. Its return value will - * be considered the next state of the tree, and the change listeners will be - * notified. + * The `reducer` function, used to create the store, will be called with the + * current state tree and the given `action`. Its return value will + * be considered the **next** state of the tree, and the change listeners + * will be notified. * * The base implementation only supports plain object actions. If you want to - * dispatch a promise, an observable, a thunk, or something else, you need to + * dispatch a Promise, an Observable, a thunk, or something else, you need to * wrap your store creating function into the corresponding middleware. For * example, see the documentation for the `redux-thunk` package. Even the * middleware will eventually dispatch plain object actions using this method. * * @param {Object} action A plain object representing “what changed”. It is * a good idea to keep actions serializable so you can record and replay user - * sessions, or use the time travelling Redux developer tools. + * sessions, or use the time travelling `redux-devtools`. * * @returns {Object} For convenience, the same action object you dispatched. + * * Note that, if you use a custom middleware, it may wrap `dispatch()` to * return something else (for example, a Promise you can await). */ @@ -125,6 +128,10 @@ export default function createStore(reducer, initialState) { dispatch({ type: ActionTypes.INIT }); } + + // When a store is created, an "INIT" action is dispatched so that every + // reducer returns their initial state. This effectively populates + // the initial state tree. dispatch({ type: ActionTypes.INIT }); return { diff --git a/src/utils/applyMiddleware.js b/src/utils/applyMiddleware.js index 734f23e5df..20bc78edf9 100644 --- a/src/utils/applyMiddleware.js +++ b/src/utils/applyMiddleware.js @@ -10,6 +10,9 @@ import compose from './compose'; * Because middleware is potentially asynchronous, this should be the first * store enhancer in the composition chain. * + * Note that each middleware will be given the `dispatch` and `getState` functions + * as named arguments. + * * @param {...Function} middlewares The middleware chain to be applied. * @returns {Function} A store enhancer applying the middleware. */ From 273b5d70c1b8f0851676ed863050c4948cab0335 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Mon, 27 Jul 2015 17:39:13 +0300 Subject: [PATCH 73/85] Update Userland and Core.md --- docs/Basics/Userland and Core.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/docs/Basics/Userland and Core.md b/docs/Basics/Userland and Core.md index 1a0cd7f973..a5012de879 100644 --- a/docs/Basics/Userland and Core.md +++ b/docs/Basics/Userland and Core.md @@ -1,7 +1,23 @@ Userland and Core -------------------------- -TODO +Modularity of the JavaScript tools and libraries is a tricky subject. As [James Long put it](http://jlongster.com/Modularity), “people have strong opinions about this because modules are how we interact with each other”. + +There is a tradeoff between doing too much in a single library versus splitting every function into a module of its own. Redux tries to stay in the middle, guided by practical considerations and the maintainers’ preferences. + +Redux started off with React bindings and some middleware included by default, but we have split those into separate packages before 1.0. Right now, this is the checklist a feature has to pass to be included in Redux core: + +* We anticipate it will be used by most Redux users in production; +* We are sure its API is good and don’t plan to introduce minor breaking changes to it; +* It doesn’t add much to the library size and introduces no new dependencies; +* It ties Redux contracts together in a beginner-friendly way; +* It plays well with extensions like [Redux DevTools](https://github.com/gaearon/redux-devtools). + +[React bindings](http://github.com/gaearon/react-redux) were separated because they add a dependency, increase the core size, but we’re still not sure if its API is good enough, and we will keep iterating on them. Moreover including them with Redux sends a wrong signal that Redux is somehow related to React, which it isn’t. + +Similarly, [redux-thunk](https://github.com/gaearon/redux-thunk) was split into a separate project because we’re not sure if it’s the best way of doing async in Redux, and we want to see more userland solutions to this problem. + +Some of the best features of Redux are implemented as libraries. For example, [reselect](https://github.com/faassen/reselect) lets you compose nested memoized selectors for super performant view updates. It’s awesome, and we might include it in the core one day when its API is stable, but for now, we want to give it the freedom to evolve without concerning itself with making breaking changes to Redux. -------------------------- Next: [Why Redux](Why Redux.md) From 6134e6ddb870f0d34bd167a62c437a90055fee59 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Mon, 27 Jul 2015 18:43:59 +0300 Subject: [PATCH 74/85] Update Migrating to Redux.md --- docs/Basics/Migrating to Redux.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/Basics/Migrating to Redux.md b/docs/Basics/Migrating to Redux.md index 70caf33512..6f7186e9c1 100644 --- a/docs/Basics/Migrating to Redux.md +++ b/docs/Basics/Migrating to Redux.md @@ -5,6 +5,8 @@ Migrating to Redux [Reducer functions](../Reference/Glossary.md#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](http://github.com/acdlite/flummox), [Alt](http://github.com/goatslacker/alt), [vanilla Flux](https://github.com/facebook/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. + Your process will look like this: * Create a function called `createFluxStore(reducer)` that creates a Flux store compatible with your existing app from a reducer function. Internally it might look similar to [`createStore`](../../src/createStore.js) implementation from Redux. Its dispatch handler should just call the `reducer` for any action, store the next state, and emit change. From a79e43649195f6a5e0e7fc261ce545d550f1715c Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Mon, 27 Jul 2015 18:44:22 +0300 Subject: [PATCH 75/85] Update Migrating to Redux.md --- docs/Basics/Migrating to Redux.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Basics/Migrating to Redux.md b/docs/Basics/Migrating to Redux.md index 6f7186e9c1..378519c2bc 100644 --- a/docs/Basics/Migrating to Redux.md +++ b/docs/Basics/Migrating to Redux.md @@ -3,7 +3,7 @@ Migrating to Redux #### From Flux Libraries -[Reducer functions](../Reference/Glossary.md#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](http://github.com/acdlite/flummox), [Alt](http://github.com/goatslacker/alt), [vanilla Flux](https://github.com/facebook/flux), or any other Flux library. +[Reducer functions](../Reference/Glossary.md#reducer) capture “the essence” of Flux Stores, so it’s possible to gradually migrate an existing Flux project towards Redux, whether you are using [Flummox](http://github.com/acdlite/flummox), [Alt](http://github.com/goatslacker/alt), [vanilla Flux](https://github.com/facebook/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. From 7b34e44ad104c651d134bea4a4f9c94e243cea16 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Mon, 27 Jul 2015 19:34:06 +0300 Subject: [PATCH 76/85] Update The Redux Flow.md --- docs/Basics/The Redux Flow.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/Basics/The Redux Flow.md b/docs/Basics/The Redux Flow.md index 030bad1119..026d1d6e34 100644 --- a/docs/Basics/The Redux Flow.md +++ b/docs/Basics/The Redux Flow.md @@ -1,7 +1,18 @@ The Redux Flow -------------------------- -TODO +Redux implements an architecture with unidirectional data flow. What does this mean? It means that **every change to the data follows the same lifecycle and goes into a single direction in a centralized fashion.** If you’re used to “fat models” or “active records” like Backbone, this may sound weird, but unidirectional data flow makes your apps much more predictable. It also encourages data normalization, so you don’t end up with independent copies of the same state that are unaware of each other. If this sounds unconvincing, read [The Case for Flux](https://medium.com/@dan_abramov/the-case-for-flux-379b7d1982c6) for a compelling argument for unidirectional data flow. Althought [Redux is not exactly Flux](./Relation to Other Libraries.md), it shares the same key benefits. + +Here is how any change in a Redux app happens: + +1. You call `store.dispatch(action)`. An `action` is just a plain object describing “what happened”. For example, `{ type: 'LIKE_ARTICLE', articleId: 42 }`, `{ type: 'USER_FETCHED', response: ... }`, or `{ type: 'ADD_TODO', text: 'Use Redux' }`. Actions are like newspapers, reporting anything that may result in changing the state of your app. You can call `store.dispatch(action)` from your components, XHR callbacks, scheduled intervals, or anywhere else. +2. The Redux store will call the [reducer function](../Reference/Glossary.md#reducer) you gave it. It will pass the current state tree as the first argument, and the action as the second one. For example, your root reducer may receive `{ todos: ['Read docs'] }` as `state` and `{ type: 'ADD_TODO', text: 'Understand the flow' }` as `action`. +3. Now, how you structure your root reducer function is completely up to you. However, Redux ships with `combineReducers` helper which is useful for “splitting” the root reducer into several separate reducer functions that each manage a slice of the state tree. Let’s imagine your passed `combineReducers({ todos: todos })` as your root reducer, where `todos` is a reducer function you wrote, and that just manages the `todos` array. The way `combineReducers` works is that, given `{ a: someFunction, b: someOtherFunction }` as its arguments, it will call `someFunction` with `state.a`, `someOtherFunction` with `state.b`, and combine their results into the new root `state`. In our example, it will pass `state.todos` as `state` to your `todos` reducer, and assemble the next root state with the same `{ todos: Array }` shape. The `todos` reducer might get `['Read docs']` as the current state, and return `['Read docs', 'Understand the flow']` as the next state. The reducer returned by `combineReducers` will then return `{ todos: ['Read docs', 'Understand the flow'] }` as the next root state. While `combineReducers` is a handy helper, you don’t have to use it, and you can write your own root reducer just fine. +4. The Redux store saves the next state tree returned by the reducer. This is now the next state of your app! It will invoke every listener registered with `store.subscribe(listener)`. The listeners may call `store.getState()` to read the current state. This is where you can update your UI using the new state. If you use bindings like [React Redux](https://github.com/gaearon/react-redux), that’s exactly where they schedule a `component.setState()` call. + +This is all there is to it. + +One important addition is that, if you use any middleware on the store, it wraps the store’s `dispatch` function and may add support for dispatching promises, [thunks](https://github.com/gaearon/redux-thunk) or other potentially asynchronous [intermediate actions](../Reference/Glossary.md#intermediate-action). In the end, after being processed with middleware, they all become “raw” plain objects, which your reducer function will receive. -------------------------- Next: [Userland and Core](Userland and Core.md) From ae1a2e8ae22558c2dd566a561185f33aecede7c9 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Mon, 27 Jul 2015 19:35:15 +0300 Subject: [PATCH 77/85] Update The Redux Flow.md --- docs/Basics/The Redux Flow.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/Basics/The Redux Flow.md b/docs/Basics/The Redux Flow.md index 026d1d6e34..15c7f2453f 100644 --- a/docs/Basics/The Redux Flow.md +++ b/docs/Basics/The Redux Flow.md @@ -5,14 +5,14 @@ Redux implements an architecture with unidirectional data flow. What does this m Here is how any change in a Redux app happens: -1. You call `store.dispatch(action)`. An `action` is just a plain object describing “what happened”. For example, `{ type: 'LIKE_ARTICLE', articleId: 42 }`, `{ type: 'USER_FETCHED', response: ... }`, or `{ type: 'ADD_TODO', text: 'Use Redux' }`. Actions are like newspapers, reporting anything that may result in changing the state of your app. You can call `store.dispatch(action)` from your components, XHR callbacks, scheduled intervals, or anywhere else. -2. The Redux store will call the [reducer function](../Reference/Glossary.md#reducer) you gave it. It will pass the current state tree as the first argument, and the action as the second one. For example, your root reducer may receive `{ todos: ['Read docs'] }` as `state` and `{ type: 'ADD_TODO', text: 'Understand the flow' }` as `action`. -3. Now, how you structure your root reducer function is completely up to you. However, Redux ships with `combineReducers` helper which is useful for “splitting” the root reducer into several separate reducer functions that each manage a slice of the state tree. Let’s imagine your passed `combineReducers({ todos: todos })` as your root reducer, where `todos` is a reducer function you wrote, and that just manages the `todos` array. The way `combineReducers` works is that, given `{ a: someFunction, b: someOtherFunction }` as its arguments, it will call `someFunction` with `state.a`, `someOtherFunction` with `state.b`, and combine their results into the new root `state`. In our example, it will pass `state.todos` as `state` to your `todos` reducer, and assemble the next root state with the same `{ todos: Array }` shape. The `todos` reducer might get `['Read docs']` as the current state, and return `['Read docs', 'Understand the flow']` as the next state. The reducer returned by `combineReducers` will then return `{ todos: ['Read docs', 'Understand the flow'] }` as the next root state. While `combineReducers` is a handy helper, you don’t have to use it, and you can write your own root reducer just fine. -4. The Redux store saves the next state tree returned by the reducer. This is now the next state of your app! It will invoke every listener registered with `store.subscribe(listener)`. The listeners may call `store.getState()` to read the current state. This is where you can update your UI using the new state. If you use bindings like [React Redux](https://github.com/gaearon/react-redux), that’s exactly where they schedule a `component.setState()` call. +1. You call `store.dispatch(action)`. An `action` is just a plain object describing “what happened”. For example, `{ type: 'LIKE_ARTICLE', articleId: 42 }`, `{ type: 'USER_FETCHED', response: ... }`, or `{ type: 'ADD_TODO', text: 'Use Redux' }`. **Actions are like newspapers, reporting anything that may result in changing the state of your app.** You can call `store.dispatch(action)` from your components, XHR callbacks, scheduled intervals, or anywhere else. +2. The Redux store will call the [reducer function](../Reference/Glossary.md#reducer) you gave it. It will **pass the current state tree as the first argument, and the action as the second one.** For example, your root reducer may receive `{ todos: ['Read docs'] }` as `state` and `{ type: 'ADD_TODO', text: 'Understand the flow' }` as `action`. +3. Now, how you structure your root reducer function is completely up to you. However, **Redux ships with `combineReducers` helper which is useful for “splitting” the root reducer into several separate reducer functions that each manage a slice of the state tree.** Let’s imagine your passed `combineReducers({ todos: todos })` as your root reducer, where `todos` is a reducer function you wrote, and that just manages the `todos` array. The way `combineReducers` works is that, given `{ a: someFunction, b: someOtherFunction }` as its arguments, it will call `someFunction` with `state.a`, `someOtherFunction` with `state.b`, and combine their results into the new root `state`. In our example, it will pass `state.todos` as `state` to your `todos` reducer, and assemble the next root state with the same `{ todos: Array }` shape. The `todos` reducer might get `['Read docs']` as the current state, and return `['Read docs', 'Understand the flow']` as the next state. The reducer returned by `combineReducers` will then return `{ todos: ['Read docs', 'Understand the flow'] }` as the next root state. While `combineReducers` is a handy helper, you don’t have to use it, and you can write your own root reducer just fine. +4. **The Redux store saves the next state tree returned by the reducer.** This is now the next state of your app! It will invoke every listener registered with `store.subscribe(listener)`. The listeners may call `store.getState()` to read the current state. This is where you can update your UI using the new state. If you use bindings like [React Redux](https://github.com/gaearon/react-redux), that’s exactly where they schedule a `component.setState()` call. This is all there is to it. -One important addition is that, if you use any middleware on the store, it wraps the store’s `dispatch` function and may add support for dispatching promises, [thunks](https://github.com/gaearon/redux-thunk) or other potentially asynchronous [intermediate actions](../Reference/Glossary.md#intermediate-action). In the end, after being processed with middleware, they all become “raw” plain objects, which your reducer function will receive. +One important addition is that, **if you use any middleware on the store, it wraps the store’s `dispatch` function** and may add support for dispatching promises, [thunks](https://github.com/gaearon/redux-thunk) or other potentially asynchronous [intermediate actions](../Reference/Glossary.md#intermediate-action). In the end, after being processed with middleware, they all become “raw” plain objects, which your reducer function will receive. -------------------------- Next: [Userland and Core](Userland and Core.md) From bbd82b4026e83e1433c716cf3ae48b3cca695074 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Mon, 27 Jul 2015 19:56:15 +0300 Subject: [PATCH 78/85] Import Getting Started by @acdlite from improve-docs branch --- docs/Basics/Getting Started.md | 247 ++++++++++++++++++++++++++++++++- 1 file changed, 246 insertions(+), 1 deletion(-) diff --git a/docs/Basics/Getting Started.md b/docs/Basics/Getting Started.md index 06d8c3418f..68a3ec3843 100644 --- a/docs/Basics/Getting Started.md +++ b/docs/Basics/Getting Started.md @@ -1,7 +1,252 @@ Getting Started -------------------------- -TODO +Don't be fooled by all the fancy talk about reducers, middleware, stores 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 + +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 a store. You send them to the store using `store.dispatch()`. + +Here's an example action which represents adding a new todo item: + +```js +{ + type: ADD_TODO, + payload: { + text: 'Build my first Redux app' + } +} +``` + +Actions are plain JavaScript objects. By convention, actions should have a `type` field that indicates the type of action being performed. Types should typically be defined as constants and imported from another module. It’s better to use strings than Symbols because strings are serializable. + +```js +import { ADD_TODO, REMOVE_TODO } from '../actionTypes'; +``` + +Other than `type`, the structure of an action object is really up to you. If you're interested, check out [Flux Standard Action](https://github.com/acdlite/flux-standard-action) for recommendations on how actions should be constructed. + +We need one more action type, for removing todos: + +```js +{ + type: REMOVE_TODO, + payload: 123 +} +``` + +`payload` in this case indicates the id of the todo we want to remove. + +### Action creators + +**Action creators** exactly that — functions that create actions. It’s easy to conflate the terms "action" and "action creator," so do your best to use the proper term. + +In *other* Flux implementations, action creators often trigger a dispatch when invoked, like so: + +```js +function addTodoWithDispatch(text) { + const action = { + type: ADD_TODO, + payload: { + text + } + }; + dispatch(action); +} +``` + +By contrast, in Redux action creators are **pure functions** with zero side-effects. They simply return an action: + +```js +function addTodo(text) { + return { + type: ADD_TODO, + payload: { + text + } + }; +} +``` + +This makes them more portable and easier to test. To actually initiate a dispatch, pass the result to the `dispatch()` function: + +```js +dispatch(addTodo(text)); +dispatch(removeTodo(id)); +``` + +Or create **bound action creator** that automatically dispatches to a particular store instance: + +```js +let boundAddTodo = text => dispatch(addTodo(text)); +let boundRemoveTodo = id => dispatch(addTodo(id)); +``` + +The `dispatch()` function can be accessed directly from the store as `store.dispatch()`, but more likely you’ll access it using a helper like `connect` from [react-redux](http://github.com/gaearon/react-redux). + +### Reducers + +Now let’s set up our store to respond to the action we defined above. + +Unlike other Flux implementations, Redux only has a single store, rather than a different store for each domain of data in your application. Instead of creating multiple stores that manually manage their own internal state, we create **reducers** that specify how to calculate a slice of the global state in response to new actions. + +A reducer looks like this: + +```js +(previousState, action) => newState +``` + +It’s the type of function you would pass to `Array.prototype.reduce(reducer, ?initialValue)`. + +This may seem radical, but it turns out that this function signature is sufficient to express the entirety of the store model from traditional Flux — in a purely functional way. **This is the essence of Redux**. It’s what enables all the cool features like hot reloading, time travel, and universal rendering. Aside from all that, though, it's simply a better model for expressing state changes. + +[**See Dan's talk at React Europe for more on this topic**](https://www.youtube.com/watch?v=xsSnOQynTHs). + +Let’s make a reducer for our Todo app: + +```js +const initialState = { todos: [], idCounter: 0 }; + +function todos(state = initialState, action) { + switch (action.type) { + case ADD_TODO: + return { + ...state, + todos: [ + ...state.todos, + { text: action.payload, id: state.idCounter + 1 } + ], + idCounter: state.idCounter + 1 + }; + case REMOVE_TODO: + return { + ...state, + todos: state.todos.filter(todo => todo.id !== action.payload) + }; + default: + return state; + } +} +``` + +Whoa, what’s going on here? A few things to note: + +- `state` is the previous state of the store. Redux will dispatch a dummy action immediately upon creating your store, at which point `state` is undefined. From that point on, `state` is the previous value returned by the reducer. +- We’re using a default parameter to specify the initial state of the store. +- We’re using a switch statement to check the action type. +- **We’re not mutating the previous state** — we're returning a **new** state object based on the **previous** state object. + +That last point is especially important: never mutate the previous state object. Always return a new state. Remember, reducers are pure functions, and should not perform mutations or side-effects. Here we’re using the ES7 spread operator to shallow copy old state values to a new object. You can use a library like Immutable.js for a nicer API and better performance, since it uses [persistent data structures](http://en.wikipedia.org/wiki/Persistent_data_structure). Here’s how that same reducer would look using immutable values: + +```js +const initialState = Immutable.Map({ todos: [], idCounter: 0 }); + +function todos(state = initialState, action) { + switch (action.type) { + case ADD_TODO: + return state + .update('todos', + todos => todos.push(Immutable.Map({ + text: action.payload, + id: state.get('idCounter') + }) + )) + .set('idCounter', state.get('idCounter') + 1); + case REMOVE_TODO: + return state + .update('todos', + todos => todos.filter(todo => todo.id !== action.payload ) + ); + default: + return state; + } +} +``` + +If you’re thinking “yuck, switch statements,” remember that reducers are just functions — you can abstract away these details using helpers. Check out [redux-actions](https://github.com/acdlite/redux-actions) for an example of how to use higher-order functions to create reducers. + +### Creating a store + +Now we have some action creators and a reducer to handle them. The next step is to create our store. + +To create a store, pass a reducer to `createStore()`: + +```js +import { createStore } from 'redux'; +import todos from '../reducers/todos'; +const store = createStore(todos); +``` + +Usually you’ll have multiple reducers for different domains of data in your app. You can use the `combineReducers()` helper to combine multiple reducers into one: + +```js +import { createStore, combineReducers } from 'redux'; +import * as reducers from '../reducers'; +const reducer = combineReducers(reducers); +const store = createStore(reducer); +``` + +For example, if the object passed to `combineReducers()` looks like this: + +```js +const reducers = { + todos: todoReducer, + counter: counterReducer +}; +``` + +It will create a reducer which produces a state object like this: + +```js +const state = { + todos: todoState, + counter: counterState +}; +``` + +### Middleware + +Middleware is an optional feature of Redux that enables you to customize how dispatches are handled. Think of middleware as a certain type of plugin or extension for Redux. + +A common use for middleware is to enable asynchronous dispatches. For example, a promise middleware adds support for dispatching promises: + +```js +dispatch(Promise.resolve({ type: ADD_TODO, payload: { text: 'Use middleware!' } })); +``` +A promise middleware would detect that a promise was dispatched, intercept it, and instead dispatch with the resolved value at a future point in time. + +Middleware is very simple to create using function composition. We won't focus on how middleware works in this document but here's how you enable it when creating your store: + +```js +import { createStore, applyMiddleware } from 'redux'; +// where promise, thunk, and observable are examples of middleware +const store = applyMiddleware(promise, thunk, observable)(createStore)(reducer); +``` + +Yes, you read that correctly. [Read more about how middleware works here.](../Recipes/Middleware.md) + +### Connecting to your views + +You’ll rarely interact with the store object directly. Most often, you’ll use some sort of binding to your preferred view library. + +Flux is most popular within the React community, but Redux works with any kind of view layer. The React bindings for Redux are available as [react-redux](github.com/gaearon/react-redux) — see that project for details on how to integrate with React. + +However, if you do find yourself needing to access the store directly, the API for doing so is very simple: + +- `store.dispatch()` dispatches an action. +- `store.getState()` gets the current state. +- `store.subscribe()` registers a listener which is called after every dispatch, and returns a function which you call to unsubscribe. + + +### Go make something great + +That’s it! As you can see, despite the powerful features that Redux enables, the core of Redux is really quite simple. + +Please let us know if you have suggestions for how this guide could be improved. -------------------------- From cd48b28cce1c6fa04a17924305fd814766f4a801 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Mon, 27 Jul 2015 20:14:56 +0300 Subject: [PATCH 79/85] less wall of text --- docs/Basics/The Redux Flow.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/Basics/The Redux Flow.md b/docs/Basics/The Redux Flow.md index 15c7f2453f..974c7e25b3 100644 --- a/docs/Basics/The Redux Flow.md +++ b/docs/Basics/The Redux Flow.md @@ -1,13 +1,16 @@ The Redux Flow -------------------------- -Redux implements an architecture with unidirectional data flow. What does this mean? It means that **every change to the data follows the same lifecycle and goes into a single direction in a centralized fashion.** If you’re used to “fat models” or “active records” like Backbone, this may sound weird, but unidirectional data flow makes your apps much more predictable. It also encourages data normalization, so you don’t end up with independent copies of the same state that are unaware of each other. If this sounds unconvincing, read [The Case for Flux](https://medium.com/@dan_abramov/the-case-for-flux-379b7d1982c6) for a compelling argument for unidirectional data flow. Althought [Redux is not exactly Flux](./Relation to Other Libraries.md), it shares the same key benefits. +Redux implements an architecture with unidirectional data flow. What does this mean? It means that **every change to the data follows the same lifecycle and goes into a single direction in a centralized fashion.** If you’re used to “fat models” or “active records” like Backbone, this may sound weird, but unidirectional data flow makes your apps much more predictable. It also encourages data normalization, so you don’t end up with independent copies of the same state that are unaware of each other. If this sounds unconvincing, read [The Case for Flux](https://medium.com/@dan_abramov/the-case-for-flux-379b7d1982c6) for a compelling argument for unidirectional data flow. Although [Redux is not exactly Flux](./Relation to Other Libraries.md), it shares the same key benefits. Here is how any change in a Redux app happens: -1. You call `store.dispatch(action)`. An `action` is just a plain object describing “what happened”. For example, `{ type: 'LIKE_ARTICLE', articleId: 42 }`, `{ type: 'USER_FETCHED', response: ... }`, or `{ type: 'ADD_TODO', text: 'Use Redux' }`. **Actions are like newspapers, reporting anything that may result in changing the state of your app.** You can call `store.dispatch(action)` from your components, XHR callbacks, scheduled intervals, or anywhere else. -2. The Redux store will call the [reducer function](../Reference/Glossary.md#reducer) you gave it. It will **pass the current state tree as the first argument, and the action as the second one.** For example, your root reducer may receive `{ todos: ['Read docs'] }` as `state` and `{ type: 'ADD_TODO', text: 'Understand the flow' }` as `action`. -3. Now, how you structure your root reducer function is completely up to you. However, **Redux ships with `combineReducers` helper which is useful for “splitting” the root reducer into several separate reducer functions that each manage a slice of the state tree.** Let’s imagine your passed `combineReducers({ todos: todos })` as your root reducer, where `todos` is a reducer function you wrote, and that just manages the `todos` array. The way `combineReducers` works is that, given `{ a: someFunction, b: someOtherFunction }` as its arguments, it will call `someFunction` with `state.a`, `someOtherFunction` with `state.b`, and combine their results into the new root `state`. In our example, it will pass `state.todos` as `state` to your `todos` reducer, and assemble the next root state with the same `{ todos: Array }` shape. The `todos` reducer might get `['Read docs']` as the current state, and return `['Read docs', 'Understand the flow']` as the next state. The reducer returned by `combineReducers` will then return `{ todos: ['Read docs', 'Understand the flow'] }` as the next root state. While `combineReducers` is a handy helper, you don’t have to use it, and you can write your own root reducer just fine. +1. You call `store.dispatch(action)`. An `action` is just a plain object describing “what happened”. For example,`{ type: 'ADD_TODO', text: 'Use Redux' }`. **Actions are like newspapers, reporting anything that may result in changing the state of your app.** You can call `store.dispatch(action)` from your components, XHR callbacks, scheduled intervals, or anywhere else. + +2. The Redux store will call the [reducer function](../Reference/Glossary.md#reducer) you gave it. It will **pass the current state tree as the first argument, and the action as the second one.** For example, your root reducer may receive `{ todos: ['Read docs'] }` as `state` and `{ type: 'ADD_TODO', text: 'Understand the flow' }` as `action`. With our example, we would expect it to return `{ todos: ['Read docs', 'Understand the flow'] }`. + +3. How you structure your root reducer function is completely up to you. However, **Redux ships with `combineReducers` helper which is useful for “splitting” the root reducer into several separate reducer functions that each manage a slice of the state tree.** This lets you write a `todos` reducer that gets `['Read docs']` as the current state, and returns `['Read docs', 'Understand the flow']` as the next state. While `combineReducers` is a handy helper, you don’t have to use it, and you can write your own root reducer just fine. + 4. **The Redux store saves the next state tree returned by the reducer.** This is now the next state of your app! It will invoke every listener registered with `store.subscribe(listener)`. The listeners may call `store.getState()` to read the current state. This is where you can update your UI using the new state. If you use bindings like [React Redux](https://github.com/gaearon/react-redux), that’s exactly where they schedule a `component.setState()` call. This is all there is to it. From d0b2ad97201a0395c1a661abc73f459e295decb7 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Mon, 27 Jul 2015 20:15:59 +0300 Subject: [PATCH 80/85] Update The Redux Flow.md --- docs/Basics/The Redux Flow.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Basics/The Redux Flow.md b/docs/Basics/The Redux Flow.md index 974c7e25b3..8e5de8964f 100644 --- a/docs/Basics/The Redux Flow.md +++ b/docs/Basics/The Redux Flow.md @@ -9,7 +9,7 @@ Here is how any change in a Redux app happens: 2. The Redux store will call the [reducer function](../Reference/Glossary.md#reducer) you gave it. It will **pass the current state tree as the first argument, and the action as the second one.** For example, your root reducer may receive `{ todos: ['Read docs'] }` as `state` and `{ type: 'ADD_TODO', text: 'Understand the flow' }` as `action`. With our example, we would expect it to return `{ todos: ['Read docs', 'Understand the flow'] }`. -3. How you structure your root reducer function is completely up to you. However, **Redux ships with `combineReducers` helper which is useful for “splitting” the root reducer into several separate reducer functions that each manage a slice of the state tree.** This lets you write a `todos` reducer that gets `['Read docs']` as the current state, and returns `['Read docs', 'Understand the flow']` as the next state. While `combineReducers` is a handy helper, you don’t have to use it, and you can write your own root reducer just fine. +3. How you structure your root reducer function is completely up to you. However, Redux ships with `combineReducers` helper which is useful for **“splitting” the root reducer into several separate reducer functions that each manage a slice of the state tree.** This lets you write a `todos` reducer that gets `['Read docs']` as the current state, and returns `['Read docs', 'Understand the flow']` as the next state. While `combineReducers` is a handy helper, you don’t have to use it, and you can write your own root reducer just fine. 4. **The Redux store saves the next state tree returned by the reducer.** This is now the next state of your app! It will invoke every listener registered with `store.subscribe(listener)`. The listeners may call `store.getState()` to read the current state. This is where you can update your UI using the new state. If you use bindings like [React Redux](https://github.com/gaearon/react-redux), that’s exactly where they schedule a `component.setState()` call. From b2bf3df0186eb9a7ad5ea7a87339b1b4ccddd45b Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Mon, 27 Jul 2015 20:17:03 +0300 Subject: [PATCH 81/85] Update The Redux Flow.md --- docs/Basics/The Redux Flow.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Basics/The Redux Flow.md b/docs/Basics/The Redux Flow.md index 8e5de8964f..6459650e3d 100644 --- a/docs/Basics/The Redux Flow.md +++ b/docs/Basics/The Redux Flow.md @@ -5,7 +5,7 @@ Redux implements an architecture with unidirectional data flow. What does this m Here is how any change in a Redux app happens: -1. You call `store.dispatch(action)`. An `action` is just a plain object describing “what happened”. For example,`{ type: 'ADD_TODO', text: 'Use Redux' }`. **Actions are like newspapers, reporting anything that may result in changing the state of your app.** You can call `store.dispatch(action)` from your components, XHR callbacks, scheduled intervals, or anywhere else. +1. You call `store.dispatch(action)`. An `action` is just a plain object describing “what happened”. For example,`{ type: 'ADD_TODO', text: 'Use Redux' }`. **Actions are like newspapers, reporting anything that might be important.** These may result in changing the state of your app. You can call `store.dispatch(action)` from your components, XHR callbacks, scheduled intervals, or anywhere else. 2. The Redux store will call the [reducer function](../Reference/Glossary.md#reducer) you gave it. It will **pass the current state tree as the first argument, and the action as the second one.** For example, your root reducer may receive `{ todos: ['Read docs'] }` as `state` and `{ type: 'ADD_TODO', text: 'Understand the flow' }` as `action`. With our example, we would expect it to return `{ todos: ['Read docs', 'Understand the flow'] }`. From 3bbf822e37a14e0f754ce1517ea3ec4cdc715b0f Mon Sep 17 00:00:00 2001 From: Dan Schuman Date: Mon, 27 Jul 2015 10:28:52 -0700 Subject: [PATCH 82/85] Update ordering or things I find this ordering to be more clear IMO. I'm fine with this PR getting rejected =P --- docs/Basics/Getting Started.md | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/docs/Basics/Getting Started.md b/docs/Basics/Getting Started.md index 68a3ec3843..296df90cee 100644 --- a/docs/Basics/Getting Started.md +++ b/docs/Basics/Getting Started.md @@ -181,25 +181,29 @@ import todos from '../reducers/todos'; const store = createStore(todos); ``` -Usually you’ll have multiple reducers for different domains of data in your app. You can use the `combineReducers()` helper to combine multiple reducers into one: +Usually you’ll have multiple reducers for different domains of data in your app. Consider the following reducers: ```js -import { createStore, combineReducers } from 'redux'; -import * as reducers from '../reducers'; -const reducer = combineReducers(reducers); -const store = createStore(reducer); -``` - -For example, if the object passed to `combineReducers()` looks like this: +// reducers/index.js +import todos as todoReducer from '../reducers/todos'; +import counter as counterReducer from '../reducers/counters'; -```js -const reducers = { +export default const reducers = { todos: todoReducer, counter: counterReducer }; ``` -It will create a reducer which produces a state object like this: +You can use the `combineReducers()` helper to combine multiple reducers into one: + +```js +import { createStore, combineReducers } from 'redux'; +import * as reducers from '../reducers'; +const reducer = combineReducers(reducers); +const store = createStore(reducer); +``` + +It will create a reducer which produces a state object, whose keys are match those of your reducers: ```js const state = { From 1b0bac44d4524cf144d30e460a611bef2e556b54 Mon Sep 17 00:00:00 2001 From: Dan Schuman Date: Mon, 27 Jul 2015 10:35:43 -0700 Subject: [PATCH 83/85] Tweaks --- docs/Basics/Getting Started.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/Basics/Getting Started.md b/docs/Basics/Getting Started.md index 296df90cee..0cb73c8981 100644 --- a/docs/Basics/Getting Started.md +++ b/docs/Basics/Getting Started.md @@ -184,14 +184,13 @@ const store = createStore(todos); Usually you’ll have multiple reducers for different domains of data in your app. Consider the following reducers: ```js -// reducers/index.js -import todos as todoReducer from '../reducers/todos'; -import counter as counterReducer from '../reducers/counters'; +export function todos(state, action) { + /* ... */ +} -export default const reducers = { - todos: todoReducer, - counter: counterReducer -}; +export function counter(state, action) { + /* ... */ +} ``` You can use the `combineReducers()` helper to combine multiple reducers into one: From 19cc4413d7608f003db160e58b2bf74d419b2ca4 Mon Sep 17 00:00:00 2001 From: Dan Schuman Date: Mon, 27 Jul 2015 11:03:52 -0700 Subject: [PATCH 84/85] Grammar fix --- docs/Basics/Getting Started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Basics/Getting Started.md b/docs/Basics/Getting Started.md index 0cb73c8981..4fbc4e025f 100644 --- a/docs/Basics/Getting Started.md +++ b/docs/Basics/Getting Started.md @@ -202,7 +202,7 @@ const reducer = combineReducers(reducers); const store = createStore(reducer); ``` -It will create a reducer which produces a state object, whose keys are match those of your reducers: +It will create a reducer which produces a state object, whose keys match those of your reducers: ```js const state = { From adf31a19147a238541bf3b5996e532d8bdf22c87 Mon Sep 17 00:00:00 2001 From: Adam Particka Date: Mon, 27 Jul 2015 13:35:11 -0500 Subject: [PATCH 85/85] Fix links in Getting Started docs --- docs/Basics/Getting Started.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Basics/Getting Started.md b/docs/Basics/Getting Started.md index 4fbc4e025f..659380f4ab 100644 --- a/docs/Basics/Getting Started.md +++ b/docs/Basics/Getting Started.md @@ -86,7 +86,7 @@ let boundAddTodo = text => dispatch(addTodo(text)); let boundRemoveTodo = id => dispatch(addTodo(id)); ``` -The `dispatch()` function can be accessed directly from the store as `store.dispatch()`, but more likely you’ll access it using a helper like `connect` from [react-redux](http://github.com/gaearon/react-redux). +The `dispatch()` function can be accessed directly from the store as `store.dispatch()`, but more likely you’ll access it using a helper like `connect` from [react-redux](https://github.com/gaearon/react-redux). ### Reducers @@ -236,7 +236,7 @@ Yes, you read that correctly. [Read more about how middleware works here.](../Re You’ll rarely interact with the store object directly. Most often, you’ll use some sort of binding to your preferred view library. -Flux is most popular within the React community, but Redux works with any kind of view layer. The React bindings for Redux are available as [react-redux](github.com/gaearon/react-redux) — see that project for details on how to integrate with React. +Flux is most popular within the React community, but Redux works with any kind of view layer. The React bindings for Redux are available as [react-redux](https://github.com/gaearon/react-redux) — see that project for details on how to integrate with React. However, if you do find yourself needing to access the store directly, the API for doing so is very simple: