diff --git a/docs/examples/todos/LICENSE.md b/docs/examples/todos/LICENSE.md
new file mode 100644
index 0000000..55bc8df
--- /dev/null
+++ b/docs/examples/todos/LICENSE.md
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015-present Dan Abramov
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/docs/examples/todos/README.md b/docs/examples/todos/README.md
new file mode 100644
index 0000000..35a04a1
--- /dev/null
+++ b/docs/examples/todos/README.md
@@ -0,0 +1,2 @@
+Code in this folder is borrowed from https://github.com/reduxjs/redux/tree/master/examples/todos
+See [LICENSE.md](./LICENSE.md) for license
diff --git a/docs/examples/todos/public/index.html b/docs/examples/todos/public/index.html
new file mode 100644
index 0000000..2e474bc
--- /dev/null
+++ b/docs/examples/todos/public/index.html
@@ -0,0 +1,21 @@
+
+
+
+
+
+ Redux Todos Example
+
+
+
+
+
+
diff --git a/docs/examples/todos/src/actions/index.js b/docs/examples/todos/src/actions/index.js
new file mode 100644
index 0000000..d10bb7a
--- /dev/null
+++ b/docs/examples/todos/src/actions/index.js
@@ -0,0 +1,22 @@
+let nextTodoId = 0
+export const addTodo = text => ({
+ type: 'ADD_TODO',
+ id: nextTodoId++,
+ text
+})
+
+export const setVisibilityFilter = filter => ({
+ type: 'SET_VISIBILITY_FILTER',
+ filter
+})
+
+export const toggleTodo = id => ({
+ type: 'TOGGLE_TODO',
+ id
+})
+
+export const VisibilityFilters = {
+ SHOW_ALL: 'SHOW_ALL',
+ SHOW_COMPLETED: 'SHOW_COMPLETED',
+ SHOW_ACTIVE: 'SHOW_ACTIVE'
+}
diff --git a/docs/examples/todos/src/actions/index.spec.js b/docs/examples/todos/src/actions/index.spec.js
new file mode 100644
index 0000000..3759659
--- /dev/null
+++ b/docs/examples/todos/src/actions/index.spec.js
@@ -0,0 +1,25 @@
+import * as actions from './index'
+
+describe('todo actions', () => {
+ it('addTodo should create ADD_TODO action', () => {
+ expect(actions.addTodo('Use Redux')).toEqual({
+ type: 'ADD_TODO',
+ id: 0,
+ text: 'Use Redux'
+ })
+ })
+
+ it('setVisibilityFilter should create SET_VISIBILITY_FILTER action', () => {
+ expect(actions.setVisibilityFilter('active')).toEqual({
+ type: 'SET_VISIBILITY_FILTER',
+ filter: 'active'
+ })
+ })
+
+ it('toggleTodo should create TOGGLE_TODO action', () => {
+ expect(actions.toggleTodo(1)).toEqual({
+ type: 'TOGGLE_TODO',
+ id: 1
+ })
+ })
+})
diff --git a/docs/examples/todos/src/components/App.js b/docs/examples/todos/src/components/App.js
new file mode 100644
index 0000000..6289393
--- /dev/null
+++ b/docs/examples/todos/src/components/App.js
@@ -0,0 +1,14 @@
+import React from 'react'
+import Footer from './Footer'
+import AddTodo from '../containers/AddTodo'
+import VisibleTodoList from '../containers/VisibleTodoList'
+
+const App = () => (
+
+)
+
+export default App
diff --git a/docs/examples/todos/src/components/Footer.js b/docs/examples/todos/src/components/Footer.js
new file mode 100644
index 0000000..60198e8
--- /dev/null
+++ b/docs/examples/todos/src/components/Footer.js
@@ -0,0 +1,20 @@
+import React from 'react'
+import FilterLink from '../containers/FilterLink'
+import { VisibilityFilters } from '../actions'
+
+const Footer = () => (
+
+ Show:
+
+ All
+
+
+ Active
+
+
+ Completed
+
+
+)
+
+export default Footer
diff --git a/docs/examples/todos/src/components/Link.js b/docs/examples/todos/src/components/Link.js
new file mode 100644
index 0000000..9ae6af4
--- /dev/null
+++ b/docs/examples/todos/src/components/Link.js
@@ -0,0 +1,22 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+
+const Link = ({ active, children, onClick }) => (
+
+ {children}
+
+)
+
+Link.propTypes = {
+ active: PropTypes.bool.isRequired,
+ children: PropTypes.node.isRequired,
+ onClick: PropTypes.func.isRequired
+}
+
+export default Link
diff --git a/docs/examples/todos/src/components/Todo.js b/docs/examples/todos/src/components/Todo.js
new file mode 100644
index 0000000..09b5b40
--- /dev/null
+++ b/docs/examples/todos/src/components/Todo.js
@@ -0,0 +1,21 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+
+const Todo = ({ onClick, completed, text }) => (
+
+ {text}
+
+)
+
+Todo.propTypes = {
+ onClick: PropTypes.func.isRequired,
+ completed: PropTypes.bool.isRequired,
+ text: PropTypes.string.isRequired
+}
+
+export default Todo
diff --git a/docs/examples/todos/src/components/TodoList.js b/docs/examples/todos/src/components/TodoList.js
new file mode 100644
index 0000000..7591db6
--- /dev/null
+++ b/docs/examples/todos/src/components/TodoList.js
@@ -0,0 +1,26 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+import Todo from './Todo'
+
+const TodoList = ({ todos, toggleTodo }) => (
+
+ {todos.map(todo =>
+ toggleTodo(todo.id)}
+ />
+ )}
+
+)
+
+TodoList.propTypes = {
+ todos: PropTypes.arrayOf(PropTypes.shape({
+ id: PropTypes.number.isRequired,
+ completed: PropTypes.bool.isRequired,
+ text: PropTypes.string.isRequired
+ }).isRequired).isRequired,
+ toggleTodo: PropTypes.func.isRequired
+}
+
+export default TodoList
diff --git a/docs/examples/todos/src/containers/AddTodo.js b/docs/examples/todos/src/containers/AddTodo.js
new file mode 100644
index 0000000..63cfc76
--- /dev/null
+++ b/docs/examples/todos/src/containers/AddTodo.js
@@ -0,0 +1,27 @@
+import React from 'react'
+import { connect } from 'react-redux'
+import { addTodo } from '../actions'
+
+const AddTodo = ({ dispatch }) => {
+ let input
+
+ return (
+
+
+
+ )
+}
+
+export default connect()(AddTodo)
diff --git a/docs/examples/todos/src/containers/FilterLink.js b/docs/examples/todos/src/containers/FilterLink.js
new file mode 100644
index 0000000..a733d21
--- /dev/null
+++ b/docs/examples/todos/src/containers/FilterLink.js
@@ -0,0 +1,16 @@
+import { connect } from 'react-redux'
+import { setVisibilityFilter } from '../actions'
+import Link from '../components/Link'
+
+const mapStateToProps = (state, ownProps) => ({
+ active: ownProps.filter === state.visibilityFilter
+})
+
+const mapDispatchToProps = (dispatch, ownProps) => ({
+ onClick: () => dispatch(setVisibilityFilter(ownProps.filter))
+})
+
+export default connect(
+ mapStateToProps,
+ mapDispatchToProps
+)(Link)
diff --git a/docs/examples/todos/src/containers/VisibleTodoList.js b/docs/examples/todos/src/containers/VisibleTodoList.js
new file mode 100644
index 0000000..1c69a9e
--- /dev/null
+++ b/docs/examples/todos/src/containers/VisibleTodoList.js
@@ -0,0 +1,30 @@
+import { connect } from 'react-redux'
+import { toggleTodo } from '../actions'
+import TodoList from '../components/TodoList'
+import { VisibilityFilters } from '../actions'
+
+const getVisibleTodos = (todos, filter) => {
+ switch (filter) {
+ case VisibilityFilters.SHOW_ALL:
+ return todos
+ case VisibilityFilters.SHOW_COMPLETED:
+ return todos.filter(t => t.completed)
+ case VisibilityFilters.SHOW_ACTIVE:
+ return todos.filter(t => !t.completed)
+ default:
+ throw new Error('Unknown filter: ' + filter)
+ }
+}
+
+const mapStateToProps = state => ({
+ todos: getVisibleTodos(state.todos, state.visibilityFilter)
+})
+
+const mapDispatchToProps = dispatch => ({
+ toggleTodo: id => dispatch(toggleTodo(id))
+})
+
+export default connect(
+ mapStateToProps,
+ mapDispatchToProps
+)(TodoList)
diff --git a/docs/examples/todos/src/index.js b/docs/examples/todos/src/index.js
new file mode 100644
index 0000000..a82802c
--- /dev/null
+++ b/docs/examples/todos/src/index.js
@@ -0,0 +1,15 @@
+import React from 'react'
+import { render } from 'react-dom'
+import { createStore } from 'redux'
+import { Provider } from 'react-redux'
+import App from './components/App'
+import rootReducer from './reducers'
+
+const store = createStore(rootReducer)
+
+render(
+
+
+ ,
+ document.getElementById('root')
+)
diff --git a/docs/examples/todos/src/reducers/index.js b/docs/examples/todos/src/reducers/index.js
new file mode 100644
index 0000000..54b8f0f
--- /dev/null
+++ b/docs/examples/todos/src/reducers/index.js
@@ -0,0 +1,8 @@
+import { combineReducers } from 'redux'
+import todos from './todos'
+import visibilityFilter from './visibilityFilter'
+
+export default combineReducers({
+ todos,
+ visibilityFilter
+})
diff --git a/docs/examples/todos/src/reducers/todos.js b/docs/examples/todos/src/reducers/todos.js
new file mode 100644
index 0000000..fc6ed32
--- /dev/null
+++ b/docs/examples/todos/src/reducers/todos.js
@@ -0,0 +1,23 @@
+const todos = (state = [], action) => {
+ switch (action.type) {
+ case 'ADD_TODO':
+ return [
+ ...state,
+ {
+ id: action.id,
+ text: action.text,
+ completed: false
+ }
+ ]
+ case 'TOGGLE_TODO':
+ return state.map(todo =>
+ (todo.id === action.id)
+ ? {...todo, completed: !todo.completed}
+ : todo
+ )
+ default:
+ return state
+ }
+}
+
+export default todos
diff --git a/docs/examples/todos/src/reducers/todos.spec.js b/docs/examples/todos/src/reducers/todos.spec.js
new file mode 100644
index 0000000..88eca35
--- /dev/null
+++ b/docs/examples/todos/src/reducers/todos.spec.js
@@ -0,0 +1,111 @@
+import todos from './todos'
+
+describe('todos reducer', () => {
+ it('should handle initial state', () => {
+ expect(
+ todos(undefined, {})
+ ).toEqual([])
+ })
+
+ it('should handle ADD_TODO', () => {
+ expect(
+ todos([], {
+ type: 'ADD_TODO',
+ text: 'Run the tests',
+ id: 0
+ })
+ ).toEqual([
+ {
+ text: 'Run the tests',
+ completed: false,
+ id: 0
+ }
+ ])
+
+ expect(
+ todos([
+ {
+ text: 'Run the tests',
+ completed: false,
+ id: 0
+ }
+ ], {
+ type: 'ADD_TODO',
+ text: 'Use Redux',
+ id: 1
+ })
+ ).toEqual([
+ {
+ text: 'Run the tests',
+ completed: false,
+ id: 0
+ }, {
+ text: 'Use Redux',
+ completed: false,
+ id: 1
+ }
+ ])
+
+ expect(
+ todos([
+ {
+ text: 'Run the tests',
+ completed: false,
+ id: 0
+ }, {
+ text: 'Use Redux',
+ completed: false,
+ id: 1
+ }
+ ], {
+ type: 'ADD_TODO',
+ text: 'Fix the tests',
+ id: 2
+ })
+ ).toEqual([
+ {
+ text: 'Run the tests',
+ completed: false,
+ id: 0
+ }, {
+ text: 'Use Redux',
+ completed: false,
+ id: 1
+ }, {
+ text: 'Fix the tests',
+ completed: false,
+ id: 2
+ }
+ ])
+ })
+
+ it('should handle TOGGLE_TODO', () => {
+ expect(
+ todos([
+ {
+ text: 'Run the tests',
+ completed: false,
+ id: 1
+ }, {
+ text: 'Use Redux',
+ completed: false,
+ id: 0
+ }
+ ], {
+ type: 'TOGGLE_TODO',
+ id: 1
+ })
+ ).toEqual([
+ {
+ text: 'Run the tests',
+ completed: true,
+ id: 1
+ }, {
+ text: 'Use Redux',
+ completed: false,
+ id: 0
+ }
+ ])
+ })
+
+})
diff --git a/docs/examples/todos/src/reducers/visibilityFilter.js b/docs/examples/todos/src/reducers/visibilityFilter.js
new file mode 100644
index 0000000..1500d4f
--- /dev/null
+++ b/docs/examples/todos/src/reducers/visibilityFilter.js
@@ -0,0 +1,12 @@
+import { VisibilityFilters } from '../actions'
+
+const visibilityFilter = (state = VisibilityFilters.SHOW_ALL, action) => {
+ switch (action.type) {
+ case 'SET_VISIBILITY_FILTER':
+ return action.filter
+ default:
+ return state
+ }
+}
+
+export default visibilityFilter