diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 00000000000..bacb43be047
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,125 @@
+# Contributing
+We are open to, and grateful for, any contributions made by the community. By contributing to Redux, you agree to abide by the [code of conduct](https://github.com/rackt/redux/blob/master/CODE_OF_CONDUCT.md).
+
+## Reporting Issues and Asking Questions
+Before opening an issue, please search the [issue tracker](https://github.com/rackt/redux/issues) to make sure your issue hasn't already been reported.
+
+Please ask any general and implementation specific questions on [Stack Overflow with a Redux tag](http://stackoverflow.com/questions/tagged/redux?sort=votes&pageSize=50) for support.
+
+## Development
+
+Visit the [Issue tracker](https://github.com/rackt/redux/issues) to find a list of open issues that need attention.
+
+Fork, then clone the repo:
+```
+git clone https://github.com/your-username/redux.git
+```
+
+### Building
+
+#### Build Redux
+
+Running the `build` task will create both a CommonJS module-per-module build and a UMD build.
+```
+npm run build
+```
+
+To create just a CommonJS module-per-module build:
+```
+npm run build:lib
+```
+
+To create just a UMD build:
+```
+npm run build:umd
+npm run build:umd:min
+```
+
+### Testing and Linting
+To run both linting and testing at once, run the following:
+```
+npm run check
+```
+
+To only run tests:
+```
+npm run test
+```
+
+To continuously watch and run tests, run the following:
+```
+npm run test:watch
+```
+
+To perform linting with `eslint`, run the following:
+```
+npm run lint
+```
+
+### Docs
+
+Improvements to the documentation are always welcome. In the docs we abide by typographic rules, so
+instead of ' you should use ’, same for “ ” and dashes (—) where appropriate. These rules only apply to the text, not to code blocks.
+
+#### Preparing to build the documentation
+To install the latest version of `gitbook` and prepare to build the documentation, run the following:
+```
+npm run docs:prepare
+```
+#### Building the documentation
+To build the documentation, run the following:
+```
+npm run docs:build
+```
+
+To watch and re-build documentation when changes occur, run the following:
+```
+npm run docs:watch
+```
+
+#### Publishing the documentation
+To publish the documentation, run the following:
+```
+npm run docs:publish
+```
+
+#### Cleaning up built documentation
+To remove previously built documentation, run the following:
+```
+npm run docs:clean
+```
+
+### Examples
+Redux comes with [official examples](http://rackt.github.io/redux/docs/introduction/Examples.html) to demonstrate various concepts and best practices.
+
+When adding a new example, please adhere to the style and format of the existing examples, and try to reuse as much code as possible. For example, `index.html`, `server.js`, and `webpack.config.js` can typically be reused.
+
+>For ease of development, the webpack configs for the examples are set up so that the `redux` module is aliased to the project `src` folder when inside of the Redux folder. If an example is moved out of the Redux folder, they will instead use the version of Redux from `node_modules`.
+
+#### Building and testing the examples
+To build and test the official Redux examples, run the following:
+```
+npm run build:examples
+npm run test:examples
+```
+
+Please visit the [Examples page](http://rackt.github.io/redux/docs/introduction/Examples.html) for information on running an individual example.
+
+###New Features
+Please open an issue with a proposal for a new feature or refactoring before starting on the work. We don't want you to waste your efforts on a pull request that we won't want to accept.
+
+###Style
+[rackt](https://github.com/rackt) is trying to keep a standard style across its various projects, which can be found over in [eslint-config-rackt](https://github.com/rackt/eslint-config-rackt). If you have a style change proposal, it should first be proposed there. If accepted, we will be happy to accept a PR to implement it here.
+
+## Submitting Changes
+* Open a new issue in the [Issue tracker](https://github.com/rackt/redux/issues).
+* Fork the repo.
+* Create a new feature branch based off the `master` branch.
+* Make sure all tests pass and there are no linting errors.
+* Submit a pull request, referencing any issues it addresses.
+
+Please try to keep your pull request focused in scope and avoid including unrelated commits.
+
+After you have submitted your pull request, we'll try to get back to you as soon as possible. We may suggest some changes or improvements.
+
+Thank you for contributing!
diff --git a/README.md b/README.md
index 49537cee035..91e7b6b29f1 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,10 @@ It is tiny (2kB) and has no dependencies.
[![npm downloads](https://img.shields.io/npm/dm/redux.svg?style=flat-square)](https://www.npmjs.com/package/redux)
[![redux channel on discord](https://img.shields.io/badge/discord-%23redux%20%40%20reactiflux-61dafb.svg?style=flat-square)](https://discord.gg/0ZcbPKXt5bZ6au5t)
[![#rackt on freenode](https://img.shields.io/badge/irc-%23rackt%20%40%20freenode-61DAFB.svg?style=flat-square)](https://webchat.freenode.net/)
+[![Changelog #187](https://img.shields.io/badge/changelog-%23187-lightgrey.svg?style=flat-square)](https://changelog.com/187)
+>**New! Learn Redux from its creator:
+>[Getting Started with Redux](https://egghead.io/series/getting-started-with-redux) (30 free videos)**
### Testimonials
@@ -111,6 +114,31 @@ If you’re coming from Flux, there is a single important difference you need to
This architecture might seem like an overkill for a counter app, but the beauty of this pattern is how well it scales to large and complex apps. It also enables very powerful developer tools, because it is possible to trace every mutation to the action that caused it. You can record user sessions and reproduce them just by replaying every action.
+### Learn Redux from Its Creator
+
+[Getting Started with Redux](https://egghead.io/series/getting-started-with-redux) is a video course consisting of 30 videos narrated by Dan Abramov, author of Redux. It is designed to complement the “Basics” part of the docs while bringing additional insights about immutability, testing, Redux best practices, and using Redux with React. **This course is free and will always be.**
+
+>[“Great course on egghead.io by @dan_abramov - instead of just showing you how to use #redux, it also shows how and why redux was built!”](https://twitter.com/sandrinodm/status/670548531422326785)
+>Sandrino Di Mattia
+
+>[“Plowing through @dan_abramov 'Getting Started with Redux' - its amazing how much simpler concepts get with video.”](https://twitter.com/chrisdhanaraj/status/670328025553219584)
+>Chris Dhanaraj
+
+>[“This video series on Redux by @dan_abramov on @eggheadio is spectacular!”](https://twitter.com/eddiezane/status/670333133242408960)
+>Eddie Zaneski
+
+>[“Come for the name hype. Stay for the rock solid fundamentals. (Thanks, and great job @dan_abramov and @eggheadio!)”](https://twitter.com/danott/status/669909126554607617)
+>Dan
+
+>[“This series of videos on Redux by @dan_abramov is repeatedly blowing my mind - gunna do some serious refactoring”](https://twitter.com/gelatindesign/status/669658358643892224)
+>Laurence Roberts
+
+So, what are you waiting for?
+
+#### [Watch the 30 Free Videos!](https://egghead.io/series/getting-started-with-redux)
+
+If you enjoyed my course, consider supporting Egghead by [buying a subscription](https://egghead.io/pricing). Subscribers have access to the source code for the example in every one of my videos, as well as to tons of advanced lessons on other topics, including JavaScript in depth, React, Angular, and more. Many [Egghead instructors](https://egghead.io/instructors) are also open source library authors, so buying a subscription is a nice way to thank them for the work that they’ve done.
+
### Documentation
* [Introduction](http://rackt.github.io/redux/docs/introduction/index.html)
diff --git a/docs/Troubleshooting.md b/docs/Troubleshooting.md
index 491624dd9d4..a4061ad398d 100644
--- a/docs/Troubleshooting.md
+++ b/docs/Troubleshooting.md
@@ -20,18 +20,20 @@ For example, a reducer like this is wrong because it mutates the state:
```js
function todos(state = [], action) {
switch (action.type) {
- case 'ADD_TODO':
- // Wrong! This mutates state
- state.push({
- text: action.text,
- completed: false
- })
- case 'COMPLETE_TODO':
- // Wrong! This mutates state[action.index].
- state[action.index].completed = true
+ case 'ADD_TODO':
+ // Wrong! This mutates state
+ state.push({
+ text: action.text,
+ completed: false
+ })
+ return state
+ case 'COMPLETE_TODO':
+ // Wrong! This mutates state[action.index].
+ state[action.index].completed = true
+ return state
+ default:
+ return state
}
-
- return state
}
```
@@ -43,7 +45,7 @@ function todos(state = [], action) {
case 'ADD_TODO':
// Return a new array
return [
- ...state,
+ ...state,
{
text: action.text,
completed: false
diff --git a/docs/advanced/AsyncActions.md b/docs/advanced/AsyncActions.md
index b223fb0a62c..5a9a34df495 100644
--- a/docs/advanced/AsyncActions.md
+++ b/docs/advanced/AsyncActions.md
@@ -45,17 +45,17 @@ We’ll use separate types in this tutorial.
## Synchronous Action Creators
-Let’s start by defining the several synchronous action types and action creators we need in our example app. Here, the user can select a reddit to display:
+Let’s start by defining the several synchronous action types and action creators we need in our example app. Here, the user can select a subreddit to display:
#### `actions.js`
```js
-export const SELECT_REDDIT = 'SELECT_REDDIT'
+export const SELECT_SUBREDDIT = 'SELECT_SUBREDDIT'
-export function selectReddit(reddit) {
+export function selectSubreddit(subreddit) {
return {
- type: SELECT_REDDIT,
- reddit
+ type: SELECT_SUBREDDIT,
+ subreddit
}
}
```
@@ -63,42 +63,42 @@ export function selectReddit(reddit) {
They can also press a “refresh” button to update it:
```js
-export const INVALIDATE_REDDIT = 'INVALIDATE_REDDIT'
+export const INVALIDATE_SUBREDDIT = 'INVALIDATE_SUBREDDIT'
-export function invalidateReddit(reddit) {
+export function invalidateSubreddit(subreddit) {
return {
- type: INVALIDATE_REDDIT,
- reddit
+ type: INVALIDATE_SUBREDDIT,
+ subreddit
}
}
```
These were the actions governed by the user interaction. We will also have another kind of action, governed by the network requests. We will see how to dispatch them later, but for now, we just want to define them.
-When it’s time to fetch the posts for some reddit, we will dispatch a `REQUEST_POSTS` action:
+When it’s time to fetch the posts for some subreddit, we will dispatch a `REQUEST_POSTS` action:
```js
export const REQUEST_POSTS = 'REQUEST_POSTS'
-export function requestPosts(reddit) {
+export function requestPosts(subreddit) {
return {
type: REQUEST_POSTS,
- reddit
+ subreddit
}
}
```
-It is important for it to be separate from `SELECT_REDDIT` or `INVALIDATE_REDDIT`. While they may occur one after another, as the app grows more complex, you might want to fetch some data independently of the user action (for example, to prefetch the most popular reddits, or to refresh stale data once in a while). You may also want to fetch in response to a route change, so it’s not wise to couple fetching to some particular UI event early on.
+It is important for it to be separate from `SELECT_SUBREDDIT` or `INVALIDATE_SUBREDDIT`. While they may occur one after another, as the app grows more complex, you might want to fetch some data independently of the user action (for example, to prefetch the most popular subreddits, or to refresh stale data once in a while). You may also want to fetch in response to a route change, so it’s not wise to couple fetching to some particular UI event early on.
Finally, when the network request comes through, we will dispatch `RECEIVE_POSTS`:
```js
export const RECEIVE_POSTS = 'RECEIVE_POSTS'
-export function receivePosts(reddit, json) {
+export function receivePosts(subreddit, json) {
return {
type: RECEIVE_POSTS,
- reddit,
+ subreddit,
posts: json.data.children.map(child => child.data),
receivedAt: Date.now()
}
@@ -123,8 +123,8 @@ Here’s what the state shape for our “Reddit headlines” app might look like
```js
{
- selectedReddit: 'frontend',
- postsByReddit: {
+ selectedSubreddit: 'frontend',
+ postsBySubreddit: {
frontend: {
isFetching: true,
didInvalidate: false,
@@ -163,7 +163,7 @@ There are a few important bits here:
>```js
> {
-> selectedReddit: 'frontend',
+> selectedSubreddit: 'frontend',
> entities: {
> users: {
> 2: {
@@ -184,7 +184,7 @@ There are a few important bits here:
> }
> }
> },
-> postsByReddit: {
+> postsBySubreddit: {
> frontend: {
> isFetching: true,
> didInvalidate: false,
@@ -215,17 +215,17 @@ Before going into the details of dispatching actions together with network reque
```js
import { combineReducers } from 'redux'
import {
- SELECT_REDDIT, INVALIDATE_REDDIT,
+ SELECT_SUBREDDIT, INVALIDATE_SUBREDDIT,
REQUEST_POSTS, RECEIVE_POSTS
} from '../actions'
-function selectedReddit(state = 'reactjs', action) {
+function selectedSubreddit(state = 'reactjs', action) {
switch (action.type) {
- case SELECT_REDDIT:
- return action.reddit
+ case SELECT_SUBREDDIT:
+ return action.subreddit
default:
return state
- }
+ }
}
function posts(state = {
@@ -234,34 +234,34 @@ function posts(state = {
items: []
}, action) {
switch (action.type) {
- case INVALIDATE_REDDIT:
- return Object.assign({}, state, {
- didInvalidate: true
- })
- case REQUEST_POSTS:
- return Object.assign({}, state, {
- isFetching: true,
- didInvalidate: false
- })
- case RECEIVE_POSTS:
- return Object.assign({}, state, {
- isFetching: false,
- didInvalidate: false,
- items: action.posts,
- lastUpdated: action.receivedAt
- })
- default:
- return state
+ case INVALIDATE_SUBREDDIT:
+ return Object.assign({}, state, {
+ didInvalidate: true
+ })
+ case REQUEST_POSTS:
+ return Object.assign({}, state, {
+ isFetching: true,
+ didInvalidate: false
+ })
+ case RECEIVE_POSTS:
+ return Object.assign({}, state, {
+ isFetching: false,
+ didInvalidate: false,
+ items: action.posts,
+ lastUpdated: action.receivedAt
+ })
+ default:
+ return state
}
}
-function postsByReddit(state = {}, action) {
+function postsBySubreddit(state = {}, action) {
switch (action.type) {
- case INVALIDATE_REDDIT:
+ case INVALIDATE_SUBREDDIT:
case RECEIVE_POSTS:
case REQUEST_POSTS:
return Object.assign({}, state, {
- [action.reddit]: posts(state[action.reddit], action)
+ [action.subreddit]: posts(state[action.subreddit], action)
})
default:
return state
@@ -269,8 +269,8 @@ function postsByReddit(state = {}, action) {
}
const rootReducer = combineReducers({
- postsByReddit,
- selectedReddit
+ postsBySubreddit,
+ selectedSubreddit
})
export default rootReducer
@@ -278,18 +278,18 @@ export default rootReducer
In this code, there are two interesting parts:
-* We use ES6 computed property syntax so we can update `state[action.reddit]` with `Object.assign()` in a terse way. This:
+* We use ES6 computed property syntax so we can update `state[action.subreddit]` with `Object.assign()` in a terse way. This:
```js
return Object.assign({}, state, {
- [action.reddit]: posts(state[action.reddit], action)
+ [action.subreddit]: posts(state[action.subreddit], action)
})
```
is equivalent to this:
```js
let nextState = {}
- nextState[action.reddit] = posts(state[action.reddit], action)
+ nextState[action.subreddit] = posts(state[action.subreddit], action)
return Object.assign({}, state, nextState)
```
* We extracted `posts(state, action)` that manages the state of a specific post list. This is just [reducer composition](../basics/Reducers.md#splitting-reducers)! It is our choice how to split the reducer into smaller reducers, and in this case, we’re delegating updating items inside an object to a `posts` reducer. The [real world example](../introduction/Examples.html#real-world) goes even further, showing how to create a reducer factory for parameterized pagination reducers.
@@ -310,18 +310,18 @@ We can still define these special thunk action creators inside our `actions.js`
import fetch from 'isomorphic-fetch'
export const REQUEST_POSTS = 'REQUEST_POSTS'
-function requestPosts(reddit) {
+function requestPosts(subreddit) {
return {
type: REQUEST_POSTS,
- reddit
+ subreddit
}
}
export const RECEIVE_POSTS = 'RECEIVE_POSTS'
-function receivePosts(reddit, json) {
+function receivePosts(subreddit, json) {
return {
type: RECEIVE_POSTS,
- reddit,
+ subreddit,
posts: json.data.children.map(child => child.data),
receivedAt: Date.now()
}
@@ -331,7 +331,7 @@ function receivePosts(reddit, json) {
// Though its insides are different, you would use it just like any other action creator:
// store.dispatch(fetchPosts('reactjs'))
-export function fetchPosts(reddit) {
+export function fetchPosts(subreddit) {
// Thunk middleware knows how to handle functions.
// It passes the dispatch method as an argument to the function,
@@ -342,7 +342,7 @@ export function fetchPosts(reddit) {
// First dispatch: the app state is updated to inform
// that the API call is starting.
- dispatch(requestPosts(reddit))
+ dispatch(requestPosts(subreddit))
// The function called by the thunk middleware can return a value,
// that is passed on as the return value of the dispatch method.
@@ -350,14 +350,14 @@ export function fetchPosts(reddit) {
// In this case, we return a promise to wait for.
// This is not required by thunk middleware, but it is convenient for us.
- return fetch(`http://www.reddit.com/r/${reddit}.json`)
+ return fetch(`http://www.reddit.com/r/${subreddit}.json`)
.then(response => response.json())
.then(json =>
// We can dispatch many times!
// Here, we update the app state with the results of the API call.
- dispatch(receivePosts(reddit, json))
+ dispatch(receivePosts(subreddit, json))
)
// In a real world app, you also want to
@@ -392,7 +392,7 @@ How do we include the Redux Thunk middleware in the dispatch mechanism? We use t
import thunkMiddleware from 'redux-thunk'
import createLogger from 'redux-logger'
import { createStore, applyMiddleware } from 'redux'
-import { selectReddit, fetchPosts } from './actions'
+import { selectSubreddit, fetchPosts } from './actions'
import rootReducer from './reducers'
const loggerMiddleware = createLogger()
@@ -404,7 +404,7 @@ const createStoreWithMiddleware = applyMiddleware(
const store = createStoreWithMiddleware(rootReducer)
-store.dispatch(selectReddit('reactjs'))
+store.dispatch(selectSubreddit('reactjs'))
store.dispatch(fetchPosts('reactjs')).then(() =>
console.log(store.getState())
)
@@ -418,34 +418,34 @@ The nice thing about thunks is that they can dispatch results of each other:
import fetch from 'isomorphic-fetch'
export const REQUEST_POSTS = 'REQUEST_POSTS'
-function requestPosts(reddit) {
+function requestPosts(subreddit) {
return {
type: REQUEST_POSTS,
- reddit
+ subreddit
}
}
export const RECEIVE_POSTS = 'RECEIVE_POSTS'
-function receivePosts(reddit, json) {
+function receivePosts(subreddit, json) {
return {
type: RECEIVE_POSTS,
- reddit,
+ subreddit,
posts: json.data.children.map(child => child.data),
receivedAt: Date.now()
}
}
-function fetchPosts(reddit) {
+function fetchPosts(subreddit) {
return dispatch => {
- dispatch(requestPosts(reddit))
- return fetch(`http://www.reddit.com/r/${reddit}.json`)
+ dispatch(requestPosts(subreddit))
+ return fetch(`http://www.reddit.com/r/${subreddit}.json`)
.then(response => response.json())
- .then(json => dispatch(receivePosts(reddit, json)))
+ .then(json => dispatch(receivePosts(subreddit, json)))
}
}
-function shouldFetchPosts(state, reddit) {
- const posts = state.postsByReddit[reddit]
+function shouldFetchPosts(state, subreddit) {
+ const posts = state.postsBySubreddit[subreddit]
if (!posts) {
return true
} else if (posts.isFetching) {
@@ -455,7 +455,7 @@ function shouldFetchPosts(state, reddit) {
}
}
-export function fetchPostsIfNeeded(reddit) {
+export function fetchPostsIfNeeded(subreddit) {
// Note that the function also receives getState()
// which lets you choose what to dispatch next.
@@ -464,9 +464,9 @@ export function fetchPostsIfNeeded(reddit) {
// a cached value is already available.
return (dispatch, getState) => {
- if (shouldFetchPosts(getState(), reddit)) {
+ if (shouldFetchPosts(getState(), subreddit)) {
// Dispatch a thunk from thunk!
- return dispatch(fetchPosts(reddit))
+ return dispatch(fetchPosts(subreddit))
} else {
// Let the calling code know there's nothing to wait for.
return Promise.resolve()
diff --git a/docs/advanced/ExampleRedditAPI.md b/docs/advanced/ExampleRedditAPI.md
index 3c55a60fccf..21677b27ed7 100644
--- a/docs/advanced/ExampleRedditAPI.md
+++ b/docs/advanced/ExampleRedditAPI.md
@@ -28,50 +28,50 @@ import fetch from 'isomorphic-fetch'
export const REQUEST_POSTS = 'REQUEST_POSTS'
export const RECEIVE_POSTS = 'RECEIVE_POSTS'
-export const SELECT_REDDIT = 'SELECT_REDDIT'
-export const INVALIDATE_REDDIT = 'INVALIDATE_REDDIT'
+export const SELECT_SUBREDDIT = 'SELECT_SUBREDDIT'
+export const INVALIDATE_SUBREDDIT = 'INVALIDATE_SUBREDDIT'
-export function selectReddit(reddit) {
+export function selectSubreddit(subreddit) {
return {
- type: SELECT_REDDIT,
- reddit
+ type: SELECT_SUBREDDIT,
+ subreddit
}
}
-export function invalidateReddit(reddit) {
+export function invalidateSubreddit(subreddit) {
return {
- type: INVALIDATE_REDDIT,
- reddit
+ type: INVALIDATE_SUBREDDIT,
+ subreddit
}
}
-function requestPosts(reddit) {
+function requestPosts(subreddit) {
return {
type: REQUEST_POSTS,
- reddit
+ subreddit
}
}
-function receivePosts(reddit, json) {
+function receivePosts(subreddit, json) {
return {
type: RECEIVE_POSTS,
- reddit,
+ subreddit,
posts: json.data.children.map(child => child.data),
receivedAt: Date.now()
}
}
-function fetchPosts(reddit) {
+function fetchPosts(subreddit) {
return dispatch => {
- dispatch(requestPosts(reddit))
- return fetch(`http://www.reddit.com/r/${reddit}.json`)
+ dispatch(requestPosts(subreddit))
+ return fetch(`http://www.reddit.com/r/${subreddit}.json`)
.then(req => req.json())
- .then(json => dispatch(receivePosts(reddit, json)))
+ .then(json => dispatch(receivePosts(subreddit, json)))
}
}
-function shouldFetchPosts(state, reddit) {
- const posts = state.postsByReddit[reddit]
+function shouldFetchPosts(state, subreddit) {
+ const posts = state.postsBySubreddit[subreddit]
if (!posts) {
return true
} else if (posts.isFetching) {
@@ -81,10 +81,10 @@ function shouldFetchPosts(state, reddit) {
}
}
-export function fetchPostsIfNeeded(reddit) {
+export function fetchPostsIfNeeded(subreddit) {
return (dispatch, getState) => {
- if (shouldFetchPosts(getState(), reddit)) {
- return dispatch(fetchPosts(reddit))
+ if (shouldFetchPosts(getState(), subreddit)) {
+ return dispatch(fetchPosts(subreddit))
}
}
}
@@ -97,14 +97,14 @@ export function fetchPostsIfNeeded(reddit) {
```js
import { combineReducers } from 'redux'
import {
- SELECT_REDDIT, INVALIDATE_REDDIT,
+ SELECT_SUBREDDIT, INVALIDATE_SUBREDDIT,
REQUEST_POSTS, RECEIVE_POSTS
} from './actions'
-function selectedReddit(state = 'reactjs', action) {
+function selectedSubreddit(state = 'reactjs', action) {
switch (action.type) {
- case SELECT_REDDIT:
- return action.reddit
+ case SELECT_SUBREDDIT:
+ return action.subreddit
default:
return state
}
@@ -116,7 +116,7 @@ function posts(state = {
items: []
}, action) {
switch (action.type) {
- case INVALIDATE_REDDIT:
+ case INVALIDATE_SUBREDDIT:
return Object.assign({}, state, {
didInvalidate: true
})
@@ -137,13 +137,13 @@ function posts(state = {
}
}
-function postsByReddit(state = { }, action) {
+function postsBySubreddit(state = { }, action) {
switch (action.type) {
- case INVALIDATE_REDDIT:
+ case INVALIDATE_SUBREDDIT:
case RECEIVE_POSTS:
case REQUEST_POSTS:
return Object.assign({}, state, {
- [action.reddit]: posts(state[action.reddit], action)
+ [action.subreddit]: posts(state[action.subreddit], action)
})
default:
return state
@@ -151,8 +151,8 @@ function postsByReddit(state = { }, action) {
}
const rootReducer = combineReducers({
- postsByReddit,
- selectedReddit
+ postsBySubreddit,
+ selectedSubreddit
})
export default rootReducer
@@ -208,7 +208,7 @@ export default class Root extends Component {
```js
import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux'
-import { selectReddit, fetchPostsIfNeeded, invalidateReddit } from '../actions'
+import { selectSubreddit, fetchPostsIfNeeded, invalidateSubreddit } from '../actions'
import Picker from '../components/Picker'
import Posts from '../components/Posts'
@@ -220,34 +220,34 @@ class AsyncApp extends Component {
}
componentDidMount() {
- const { dispatch, selectedReddit } = this.props
- dispatch(fetchPostsIfNeeded(selectedReddit))
+ const { dispatch, selectedSubreddit } = this.props
+ dispatch(fetchPostsIfNeeded(selectedSubreddit))
}
componentWillReceiveProps(nextProps) {
- if (nextProps.selectedReddit !== this.props.selectedReddit) {
- const { dispatch, selectedReddit } = nextProps
- dispatch(fetchPostsIfNeeded(selectedReddit))
+ if (nextProps.selectedSubreddit !== this.props.selectedSubreddit) {
+ const { dispatch, selectedSubreddit } = nextProps
+ dispatch(fetchPostsIfNeeded(selectedSubreddit))
}
}
- handleChange(nextReddit) {
- this.props.dispatch(selectReddit(nextReddit))
+ handleChange(nextSubreddit) {
+ this.props.dispatch(selectSubreddit(nextSubreddit))
}
handleRefreshClick(e) {
e.preventDefault()
- const { dispatch, selectedReddit } = this.props
- dispatch(invalidateReddit(selectedReddit))
- dispatch(fetchPostsIfNeeded(selectedReddit))
+ const { dispatch, selectedSubreddit } = this.props
+ dispatch(invalidateSubreddit(selectedSubreddit))
+ dispatch(fetchPostsIfNeeded(selectedSubreddit))
}
render() {
- const { selectedReddit, posts, isFetching, lastUpdated } = this.props
+ const { selectedSubreddit, posts, isFetching, lastUpdated } = this.props
return (
-
@@ -281,7 +281,7 @@ class AsyncApp extends Component {
}
AsyncApp.propTypes = {
- selectedReddit: PropTypes.string.isRequired,
+ selectedSubreddit: PropTypes.string.isRequired,
posts: PropTypes.array.isRequired,
isFetching: PropTypes.bool.isRequired,
lastUpdated: PropTypes.number,
@@ -289,18 +289,18 @@ AsyncApp.propTypes = {
}
function mapStateToProps(state) {
- const { selectedReddit, postsByReddit } = state
+ const { selectedSubreddit, postsBySubreddit } = state
const {
isFetching,
lastUpdated,
items: posts
- } = postsByReddit[selectedReddit] || {
+ } = postsBySubreddit[selectedSubreddit] || {
isFetching: true,
items: []
}
return {
- selectedReddit,
+ selectedSubreddit,
posts,
isFetching,
lastUpdated
diff --git a/docs/api/Store.md b/docs/api/Store.md
index 326f91a795e..fff1db31df1 100644
--- a/docs/api/Store.md
+++ b/docs/api/Store.md
@@ -47,7 +47,7 @@ The store’s reducing function will be called with the current [`getState()`](#
#### Returns
-(Object†): The dispatched action.
+(Object†): The dispatched action (see notes).
#### Notes
diff --git a/docs/api/combineReducers.md b/docs/api/combineReducers.md
index 7748aab461d..01492a128c8 100644
--- a/docs/api/combineReducers.md
+++ b/docs/api/combineReducers.md
@@ -4,7 +4,20 @@ As your app grows more complex, you’ll want to split your [reducing function](
The `combineReducers` helper function turns an object whose values are different reducing functions into a single reducing function you can pass to [`createStore`](createStore.md).
-The resulting reducer calls every child reducer, and gather their results into a single state object. The shape of the state object matches the keys of the passed `reducers`.
+The resulting reducer calls every child reducer, and gathers their results into a single state object. **The shape of the state object matches the keys of the passed `reducers`**.
+
+Consequently, the state object will look like this:
+
+```
+{
+ reducer1: ...
+ reducer2: ...
+}
+```
+
+You can control state key names by using different keys for the reducers in the passed object. For example, you may call `combineReducers({ todos: myTodosReducer, counter: myCounterReducer })` for the state shape to be `{ todos, counter }`.
+
+A popular convention is to name reducers after the state slices they manage, so you can use ES6 property shorthand notation: `combineReducers({ counter, todos })`. This is equivalent to writing `combineReducers({ counter: counter, todos: todos })`.
> ##### A Note for Flux Users
diff --git a/docs/basics/Actions.md b/docs/basics/Actions.md
index 2501ecc8279..2c47f417cf0 100644
--- a/docs/basics/Actions.md
+++ b/docs/basics/Actions.md
@@ -65,7 +65,7 @@ function addTodoWithDispatch(text) {
}
```
-By contrast, in Redux action creators are **pure functions** with zero side-effects. They simply return an action:
+By contrast, in Redux action creators simply return an action:
```js
function addTodo(text) {
diff --git a/docs/basics/ExampleTodoList.md b/docs/basics/ExampleTodoList.md
index dc2413871b8..e83e4cdd6f5 100644
--- a/docs/basics/ExampleTodoList.md
+++ b/docs/basics/ExampleTodoList.md
@@ -52,12 +52,18 @@ export const VisibilityFilters = {
* action creators
*/
+let nextTodoId = 0;
+
export function addTodo(text) {
- return { type: ADD_TODO, text }
+ return {
+ type: ADD_TODO,
+ id: nextTodoId++,
+ text
+ };
}
-export function completeTodo(index) {
- return { type: COMPLETE_TODO, index }
+export function completeTodo(id) {
+ return { type: COMPLETE_TODO, id }
}
export function setVisibilityFilter(filter) {
@@ -83,24 +89,39 @@ function visibilityFilter(state = SHOW_ALL, action) {
}
}
+function todo(state, action) {
+ switch (action.type) {
+ case ADD_TODO:
+ return {
+ id: action.id,
+ text: action.text,
+ completed: false
+ }
+ case COMPLETE_TODO:
+ if (state.id !== action.id) {
+ return state
+ }
+
+ return {
+ ...state,
+ completed: true
+ }
+ default:
+ return state
+ }
+}
+
function todos(state = [], action) {
switch (action.type) {
case ADD_TODO:
return [
...state,
- {
- text: action.text,
- completed: false
- }
+ todo(undefined, action)
]
case COMPLETE_TODO:
- return [
- ...state.slice(0, action.index),
- Object.assign({}, state[action.index], {
- completed: true
- }),
- ...state.slice(action.index + 1)
- ]
+ return state.map(t =>
+ todo(t, action)
+ )
default:
return state
}
@@ -138,8 +159,8 @@ class App extends Component {
} />
- dispatch(completeTodo(index))
+ onTodoClick={id =>
+ dispatch(completeTodo(id))
} />