[Redux] Extracting Container Components -- Complete
Clean TodoApp Component, it doesn't need to receive any props from the top level component:
const TodoApp = () => ( <div> <AddTodo /> <VisibleTodoList /> <Footer /> </div> );
Also we don't need wrap ReactDOM.render() into a render function and subscribe it, because the container component will update itself when state changes:
// From const render = () => { ReactDOM.render( <TodoApp {...store.getState()} />, document.getElementById('root') ); }; store.subscribe(render); render(); // To ReactDOM.render( <TodoApp {...store.getState()} />, document.getElementById('root') );
The first component I'm looking at is called AddToDo. Frankly, I cant classify it either as a presentational component or as a container component because it doesn't fit either category. The input and the button are the presentational part, but dispatching an action onClick is the behavior which is usually specified by the container.
However, in this case, I'd rather keep them together because there isn't any state, the UI is very simple. It's hard to imagine any other behavior other than dispatching the AddToDo action.
-----
In general, I suggest first trying to extract the presentational components. If there is too much boilerplate passing the props through them, then you can create the containers around them that load the data and specify the behavior.
--------------
Code:
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 { createStore } = Redux; const store = createStore(todoApp); 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() { this.unsubscribe = store.subscribe(() => this.forceUpdate() ); } componentWillUnmount() { this.unsubscribe(); } render() { const props = this.props; const state = store.getState(); return ( <Link active={ props.filter === state.visibilityFilter } onClick={() => store.dispatch({ type: 'SET_VISIBILITY_FILTER', filter: props.filter }) } > {props.children} </Link> ); } } 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> ); const AddTodo = ({ onAddClick }) => { 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> ); }; 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() { this.unsubscribe = store.subscribe(() => this.forceUpdate() ); } componentWillUnmount() { this.unsubscribe(); } render() { const props = this.props; const state = store.getState(); return ( <TodoList todos={ getVisibleTodos( state.todos, state.visibilityFilter ) } onTodoClick={id => store.dispatch({ type: 'TOGGLE_TODO', id }) } /> ); } } let nextTodoId = 0; const TodoApp = () => ( <div> <AddTodo /> <VisibleTodoList /> <Footer /> </div> ); ReactDOM.render( <TodoApp {...store.getState()} />, document.getElementById('root') );
相关文章
- 小白学数据分析--留存率分析_I次日留存率突然下降了50%
- java算法 -- 冒泡排序
- 第1章 1.8计算机网络概述--OSI参考模型和网络排错
- 读书笔记--SQL必知必会02--检索数据
- [Redux] Accessing Dispatch and State with Redux -- connect
- [Redux] Extracting Presentational Components -- Footer, FilterLink
- [Intro to Deep Learning with PyTorch -- L2 -- N24] Logistic Regression Algorithm
- [AngularJS NG-redux] Integrate Redux Devtools
- [AngualrJS NG-redux] Map State and Dispatchers to Redux
- 1.第一章 计算机基础和Linux安装 -- 计算机系统(一)
- L37.linux命令每日一练 -- 第六章 文件备份与压缩命令 -- tar和gzip
- 老板叫我写个APP自动化--Yaml文件读取--内附整个框架源码
- LVS管理工具--ipvsadm
- 超级赛亚人悟空与弗利沙的战斗篇--状态模式