Previously, we wrote the Provider component by ourself:
class Provider extends Component { getChildContext() { return { store: this.props.store }; } render() { return this.props.children; } } Provider.childContextTypes = { store: React.PropTypes.object };
Then we can get 'store' from the 'context' from each child container component:
class VisibleTodoList extends Component { componentDidMount() { const { store } = this.context; this.unsubscribe = store.subscribe(() => this.forceUpdate() ); } componentWillUnmount() { this.unsubscribe(); } render() { const props = this.props; const { store } = this.context; const state = store.getState(); return ( <TodoList todos={ getVisibleTodos( state.todos, state.visibilityFilter ) } onTodoClick={id => store.dispatch({ type: 'TOGGLE_TODO', id }) } /> ); } } VisibleTodoList.contextTypes = { store: React.PropTypes.object };
or Persentiaonal component:
const AddTodo = (props, { store }) => { let input; return ( <div> <input ref={node => { input = node; }} /> <button onClick={() => { store.dispatch({ type: 'ADD_TODO', id: nextTodoId++, text: input.value }) input.value = ''; }}> Add Todo </button> </div> ); }; AddTodo.contextTypes = { store: React.PropTypes.object };
Because the solution is so convenient, we can use library instead of writing our own Provider:
So we can remvoe all the whole Provider class component, instead of we do:
const {Provider} = ReactRedux;
In html:
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/4.0.0/react-redux.js"></script>
---------
Code:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>JS Bin</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/redux/3.0.4/redux.js"></script> <script src="https://fb.me/react-0.14.0.js"></script> <script src="https://fb.me/react-dom-0.14.0.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/4.0.0/react-redux.js"></script> </head> <body> <div id='root'></div> </body> </html>
const todo = (state, action) => { switch (action.type) { case 'ADD_TODO': return { id: action.id, text: action.text, completed: false }; case 'TOGGLE_TODO': if (state.id !== action.id) { return state; } return { ...state, completed: !state.completed }; default: return state; } }; const todos = (state = [], action) => { switch (action.type) { case 'ADD_TODO': return [ ...state, todo(undefined, action) ]; case 'TOGGLE_TODO': return state.map(t => todo(t, action) ); default: return state; } }; const visibilityFilter = ( state = 'SHOW_ALL', action ) => { switch (action.type) { case 'SET_VISIBILITY_FILTER': return action.filter; default: return state; } }; const { combineReducers } = Redux; const todoApp = combineReducers({ todos, visibilityFilter }); const { Component } = React; const Link = ({ active, children, onClick }) => { if (active) { return <span>{children}</span>; } return ( <a href='#' onClick={e => { e.preventDefault(); onClick(); }} > {children} </a> ); }; class FilterLink extends Component { componentDidMount() { const { store } = this.context; this.unsubscribe = store.subscribe(() => this.forceUpdate() ); } componentWillUnmount() { this.unsubscribe(); } render() { const props = this.props; const { store } = this.context; const state = store.getState(); return ( <Link active={ props.filter === state.visibilityFilter } onClick={() => store.dispatch({ type: 'SET_VISIBILITY_FILTER', filter: props.filter }) } > {props.children} </Link> ); } } FilterLink.contextTypes = { store: React.PropTypes.object }; const Footer = () => ( <p> Show: {' '} <FilterLink filter='SHOW_ALL' > All </FilterLink> {', '} <FilterLink filter='SHOW_ACTIVE' > Active </FilterLink> {', '} <FilterLink filter='SHOW_COMPLETED' > Completed </FilterLink> </p> ); const Todo = ({ onClick, completed, text }) => ( <li onClick={onClick} style={{ textDecoration: completed ? 'line-through' : 'none' }} > {text} </li> ); const TodoList = ({ todos, onTodoClick }) => ( <ul> {todos.map(todo => <Todo key={todo.id} {...todo} onClick={() => onTodoClick(todo.id)} /> )} </ul> ); let nextTodoId = 0; const AddTodo = (props, { store }) => { let input; return ( <div> <input ref={node => { input = node; }} /> <button onClick={() => { store.dispatch({ type: 'ADD_TODO', id: nextTodoId++, text: input.value }) input.value = ''; }}> Add Todo </button> </div> ); }; AddTodo.contextTypes = { store: React.PropTypes.object }; const getVisibleTodos = ( todos, filter ) => { switch (filter) { case 'SHOW_ALL': return todos; case 'SHOW_COMPLETED': return todos.filter( t => t.completed ); case 'SHOW_ACTIVE': return todos.filter( t => !t.completed ); } } class VisibleTodoList extends Component { componentDidMount() { const { store } = this.context; this.unsubscribe = store.subscribe(() => this.forceUpdate() ); } componentWillUnmount() { this.unsubscribe(); } render() { const props = this.props; const { store } = this.context; const state = store.getState(); return ( <TodoList todos={ getVisibleTodos( state.todos, state.visibilityFilter ) } onTodoClick={id => store.dispatch({ type: 'TOGGLE_TODO', id }) } /> ); } } VisibleTodoList.contextTypes = { store: React.PropTypes.object }; const TodoApp = () => ( <div> <AddTodo /> <VisibleTodoList /> <Footer /> </div> ); const {Provider} = ReactRedux; const { createStore } = Redux; ReactDOM.render( <Provider store={createStore(todoApp)}> <TodoApp /> </Provider>, document.getElementById('root') );