Learn how to avoid the boilerplate of passing the props down the intermediate components by introducing more container components.

Code to be refactored:

const FilterLink = ({
filter,
currentFilter,
children,
onClick
}) => {
if (filter === currentFilter) {
return <span>{children}</span>;
} return (
<a href='#'
onClick={e => {
e.preventDefault();
onClick(filter);
}}
>
{children}
</a>
);
}; const Footer = ({
visibilityFilter,
onFilterClick
}) => (
<p>
Show:
{' '}
<FilterLink
filter='SHOW_ALL'
currentFilter={visibilityFilter}
onClick={onFilterClick}
>
All
</FilterLink>
{', '}
<FilterLink
filter='SHOW_ACTIVE'
currentFilter={visibilityFilter}
onClick={onFilterClick}
>
Active
</FilterLink>
{', '}
<FilterLink
filter='SHOW_COMPLETED'
currentFilter={visibilityFilter}
onClick={onFilterClick}
>
Completed
</FilterLink>
</p>
); const TodoApp = ({
todos,
visibilityFilter
}) => (
<div>
...
...
...
<Footer
visibilityFilter={visibilityFilter}
onFilterClick={filter =>
store.dispatch({
type: 'SET_VISIBILITY_FILTER',
filter
})
}
/>
</div>
);

Notice in the container component, we pass visibilityFilter to the Footer presentional component, but Footer component actually doesn't do anything about that, just pass down to the FilterLink presentional component. This is the downside for the current approach.

TodoApp (C) --> Footer (P) --> FilterLink (P)

If we met this kind of problem, what we can do is, break FilterLink into:

FilterLink (C) --> Link (P)

We convent FilterLink into a container component and make a new presentional component called 'Link'.

And inside FilterLink, we can use Redux to getState(), everytime the state change, we force the component render itself and remember to unsubscribe when the component will unmount.

Also we move the dispatch action from TodoApp container component to the FilterLink container component. So that in TodoApp, the Footer component looks nice and clean.

So now, it looks like:

TodoApp (C) --> Footer (P) --> FilterLink (C) --> Link (P)

Link (P):

const Link = ({
active,
children,
onClick
}) => {
if (active) {
return <span>{children}</span>;
} return (
<a href='#'
onClick={e => {
e.preventDefault();
onClick();
}}
>
{children}
</a>
);
};

FilterLink (C):

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

Footer (P):

const Footer = () => (
<p>
Show:
{' '}
<FilterLink
filter='SHOW_ALL'
>
All
</FilterLink>
{', '}
<FilterLink
filter='SHOW_ACTIVE'
>
Active
</FilterLink>
{', '}
<FilterLink
filter='SHOW_COMPLETED'
>
Completed
</FilterLink>
</p>
);

All:

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={() => {
onAddClick(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
);
}
} let nextTodoId = 0;
const TodoApp = ({
todos,
visibilityFilter
}) => (
<div>
<AddTodo
onAddClick={text =>
store.dispatch({
type: 'ADD_TODO',
id: nextTodoId++,
text
})
}
/>
<TodoList
todos={
getVisibleTodos(
todos,
visibilityFilter
)
}
onTodoClick={id =>
store.dispatch({
type: 'TOGGLE_TODO',
id
})
}
/>
<Footer />
</div>
); const render = () => {
ReactDOM.render(
<TodoApp
{...store.getState()}
/>,
document.getElementById('root')
);
}; store.subscribe(render);
render();

[Redux] Extracting Container Components (FilterLink)的更多相关文章

  1. [Redux] Extracting Container Components -- Complete

    Clean TodoApp Component, it doesn't need to receive any props from the top level component: const To ...

  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 -- Footer, FilterLink

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

  5. [Redux] Extracting Presentational Components -- AddTodo

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

  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. 定时改变App主题的方案

    1.将接口返回的图片缓存到本地,由于写data到本地是耗时操作,为了不阻塞主线程,可开启子线程来做此操作 dispatch_queue_t queue = dispatch_queue_create( ...

  2. SpringMvc之@RequestParam详解

    @RequestParam是传递参数的. @RequestParam用于将请求参数区数据映射到功能处理方法的参数上. public String queryUserName(@RequestParam ...

  3. Quartz实现定时任务的配置方法

    1.   CronTrigger时间格式配置说明 CronTrigger配置格式: 格式: [秒] [分] [小时] [日] [月] [周] [年] 序号 说明 是否必填 允许填写的值 允许的通配符 ...

  4. 实现类似QQ的折叠效果

    //  主要核心是点击自定义header来展开和收起每一组里面的cell,模型里面应该有isShow此属性来记录开展还是收起. //  ViewController.m//  实现类似QQ的折叠效果/ ...

  5. Swift中可能失败的构造器的传播(调用)和重写

    import Foundation /* 可能失败构造器的传播(调用) 1.可能失败的构造器可以调用同一个类中的普通构造器 2.普通构造器不能调用同一个类中的可能失败构造器 3.结构体中, 普通构造器 ...

  6. 求解:C#.Net 远程方法调用失败 (Exception from HRESULT: 0x800706BE)

    服务器:Windows Server2003 sp2服务器 客户端:XP SP3 内容:C#Winform客户端调用服务器的Excel模板生成报表的时候,生成失败,抛出的异常如下: TargetInv ...

  7. HTML&CSS基础学习笔记1.17-表格的头部与尾部

    表格的头部和尾部 既然有标签表示表格的主体,那么自然表格的头部和尾部也有对应的标签. HTML中使用<thead>标签表示表格的头部,使用<tfoot>标签表示表格的尾部. 有 ...

  8. C程序设计语言练习题1-9

    练习1-9 编写一个将输入复制到输出的程序,并将其中连续的多个空格用一个空格代替. 代码如下: #include <stdio.h> // 包含标准库的信息. int main() // ...

  9. C语言初学 比较五个整数并输出最大值和最小值2

    #include <stdio.h> int main() { int i,a[5]; int max = 0,min = 0; printf("输入5个整数(空格隔开) :\n ...

  10. fgets和scanf的区别

    fgets和scanf的区别 1.测试使用scanf的一个例子: #include "stdio.h" #include "string.h" int main ...