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')
);

[Redux] Extracting Container Components -- Complete的更多相关文章

  1. [Redux] Extracting Container Components (FilterLink)

    Learn how to avoid the boilerplate of passing the props down the intermediate components by introduc ...

  2. [Redux] Extracting Container Components -- VisibleTodoList

    Code to be refacted: const TodoList = ({ todos, onTodoClick }) => ( <ul> {todos.map(todo =& ...

  3. [Redux] Redux: Extracting Container Components -- AddTodo

    Code to be refactored: const AddTodo = ({ onAddClick }) => { let input; return ( <div> < ...

  4. [Redux] Extracting Presentational Components -- AddTodo

    The code to be refactored: let nextTodoId = 0; class TodoApp extends Component { render() { const { ...

  5. [Redux] Extracting Presentational Components -- Footer, FilterLink

    Code to be refactored: let nextTodoId = 0; class TodoApp extends Component { render() { const { todo ...

  6. [Redux] Extracting Presentational Components -- TodoApp

    Finally, I just noticed that the to-do app component doesn't actually have to be a class. I can turn ...

  7. [Redux] Extracting Presentational Components -- Todo, TodoList

    Code to be refactored: let nextTodoId = 0; class TodoApp extends Component { render() { const { todo ...

  8. Presentational and Container Components

    https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0 There’s a simple pattern I fi ...

  9. (翻译)React Container Components

    原文:Container Components Container Components 在 React 模式上对我的代码有最深远影响的一个模式叫 container component 模式. 在 ...

随机推荐

  1. 论js闭包的重要性

    很久没写博客了,今天发现了一个很有意思的问题,写下来分享一下 话不多说,贴前端代码: <script type="text/javascript" src="js/ ...

  2. Eclipse中如何安装和使用GrepCode插件

    GrepCode(GC)Eclipse插件允许Eclipse用户在Eclipse IDE中搜索由GrepCode提供的工厂类.本教程介绍如何安装和使用插件.使用Eclipse3.5(Galileo)的 ...

  3. 【iOS之runtime、runloop】

    什么是runtime runtime就是运行时,是系统在运行时的一些动态机制,它是一套底层的API,我们平时编写的OC代码,最终会转换为runtime实现. runtime的作用 可以利用runtim ...

  4. 自定义控件 闪烁效果的TextView

    使用 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"     android ...

  5. for-of循环

    /* 1. 遍历数组 2. 遍历Set 3. 遍历Map 4. 遍历字符串 5. 遍历伪数组 6. 可迭代的对象 */var arr = [2,3,4];for(let ele of arr) { c ...

  6. bootstrap table使用小记

    bootstrap table是一个非常不错的,基于bootstrap的插件,它扩展和丰富了bootstrap表格的操作,如格式化表格,表格选择器,表格工具栏,分页等等. 最近基于bootstrap开 ...

  7. 使用 Intel HAXM 为 Android 模拟器加速,媲美真机(转)

    源:http://www.cnblogs.com/beginor/archive/2013/01/13/2858228.html 慢的问题. Intel HAXM (Hardware Accelera ...

  8. (转)SQL Server 2008怎样编辑200行以上的数据

    刚换SQL Server2008 不久,感觉运行速度.编辑提示都比05版的提升不少,但是在维护考试系统中遇到一个05中没有的问题:05中有“打开表”可以编辑所有数据行,到了08变成了“打开前1000行 ...

  9. struts2 JS获取上传文件的绝对路径,兼容IE和FF

    因为file控件上传失败后会自动清空,所以使用文本框来保存上传路径,而且在不同的浏览器下,控件的样式也需要兼容.下面是自己用到的实例 // 初始化判断浏览器的版本,根据版本的不同使用不同的样式func ...

  10. polya定理小结

    polya的精髓就在与对循环节的寻找,其中常遇到的问题就是项链染色类问题. 当项链旋转时有n种置换,循环节的个数分别是gcd(n, i); 当项链翻转时有n种置换,其中当项链珠子数位奇数时,循环节的个 ...