[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')
);
[Redux] Extracting Container Components -- Complete的更多相关文章
- [Redux] Extracting Container Components (FilterLink)
Learn how to avoid the boilerplate of passing the props down the intermediate components by introduc ...
- [Redux] Extracting Container Components -- VisibleTodoList
Code to be refacted: const TodoList = ({ todos, onTodoClick }) => ( <ul> {todos.map(todo =& ...
- [Redux] Redux: Extracting Container Components -- AddTodo
Code to be refactored: const AddTodo = ({ onAddClick }) => { let input; return ( <div> < ...
- [Redux] Extracting Presentational Components -- AddTodo
The code to be refactored: let nextTodoId = 0; class TodoApp extends Component { render() { const { ...
- [Redux] Extracting Presentational Components -- Footer, FilterLink
Code to be refactored: let nextTodoId = 0; class TodoApp extends Component { render() { const { todo ...
- [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 ...
- [Redux] Extracting Presentational Components -- Todo, TodoList
Code to be refactored: let nextTodoId = 0; class TodoApp extends Component { render() { const { todo ...
- Presentational and Container Components
https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0 There’s a simple pattern I fi ...
- (翻译)React Container Components
原文:Container Components Container Components 在 React 模式上对我的代码有最深远影响的一个模式叫 container component 模式. 在 ...
随机推荐
- Java基础知识强化41:StringBuffer类之StringBuffer的反转功能
1. StringBuffer 的反转功能: public StringBuffer reverse(): 2. 案例演示: package cn.itcast_05; /* * StringBuff ...
- CentOS6.7 用户
1.添加普通用户[root@server ~]# useradd chenjiafa //添加一个名为chenjiafa的用户[root@server ~]# passwd chenjiafa ...
- firefox 不能显示 glyphicons 字体
折腾了很久才发现是firefox 不能跨域下载相应的字体文件,将bootstrap相应的css文件和字体文件copy到调用的项目里,问题才得以解决.
- servlet的含义和作用
servle(Server Applet):全称Java Servlet,未有中文译文.是用Java编写的服务器端程序.其主要功能在于交互式地浏览和修改数据,生成动态Web内容.狭义的Servlet是 ...
- (转)asp.net(C#)手记之Repeater与两级菜单
先来张图片说明下我们要实现的菜单: 这个菜单只实现了2级哈. 我采用的方法是嵌套2个Repeater. 先看下数据库中的表结构: 数据: 上代码: aspx: <asp:Repeater ID= ...
- (转)WCF入门教程(一)简介
原文系列来自http://www.cnblogs.com/yank/p/3653160.html 1.WCF是什么? WCF( Windows Communication Foundation), 是 ...
- UML基础知识
UML:Unified Modeling Language,即统一建模语言.是一种图形化的建模语言标准. 如上图,UML可以帮助我们做软件需求分析和软件设计两方面的工作,在不同的应用场景中,UML的一 ...
- Fragment实现底部Tab,切换可保存状态
activity_main.xml <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android& ...
- 服务器重启后Oracle监听服务没有自动启动的解决方案
最近一直在被这样一个问题烦恼,就是服务器断电重启后,Oracle监听服务没有正常自动启动(监听服务已经设置为自启动). 具体是这样的,监听服务设置为开机自启动,Oracle数据库服务设置为开机延时启动 ...
- 使用angularjs中ng-repeat的$even与$odd属性时的注意事项
JavaScript中数组的索引是从0开始的,因此我们再取奇偶的时候需要用!$even和!$odd来将$even和$odd的布尔值反转 下面给出一个实例: 使用$odd和$even来制作一个红蓝相间的 ...