[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 it into a function. I prefer to-do that when possible.

Code to be refactored:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
class TodoApp extends Component {
  render() {
    const {
      todos,
      visibilityFilter
    } = this.props;
    const visibleTodos = getVisibleTodos(
      todos,
      visibilityFilter
    );
    return (
      <div>
        <input ref={node => {
          this.input = node;
        }} />
        <button onClick={() => {
          store.dispatch({
            type: 'ADD_TODO',
            text: this.input.value,
            id: nextTodoId++
          });
          this.input.value = '';
        }}>
          Add Todo
        </button>
        <ul>
          {visibleTodos.map(todo =>
            <li key={todo.id}
                onClick={() => {
                  store.dispatch({
                    type: 'TOGGLE_TODO',
                    id: todo.id
                  });        
                }}
                style={{
                  textDecoration:
                    todo.completed ?
                      'line-through' :
                      'none'
                }}>
              {todo.text}
            </li>
          )}
        </ul>
        <p>
          Show:
          {' '}
          <FilterLink
            filter='SHOW_ALL'
            currentFilter={visibilityFilter}
          >
            All
          </FilterLink>
          {', '}
          <FilterLink
            filter='SHOW_ACTIVE'
            currentFilter={visibilityFilter}
          >
            Active
          </FilterLink>
          {', '}
          <FilterLink
            filter='SHOW_COMPLETED'
            currentFilter={visibilityFilter}
          >
            Completed
          </FilterLink>
        </p>
      </div>
    );
  }
}

  

TO:

 

复制代码
const TodoApp = ({
  todos,
  visibilityFilter
}) => {
  return (
      <div>
        <AddTodo 
           onAddTodo={ text =>
              store.dispatch({
                  type: 'ADD_TODO',
                  id: nextTodoId++,
                  text
              })
           }
        />
        <TodoList 
          todos={getVisibleTodos(
              todos,
              visibilityFilter
          )}
          onTodoClick={
            (id)=>{
              store.dispatch({
                type: 'TOGGLE_TODO',
                id
              })
            }
          }
        />
        <Footer 
           visibilityFilter = {visibilityFilter}
           onFilterClick={ (filter) => {
              store.dispatch({
                 type: 'SET_VISIBILITY_FILTER',
                 filter
             });
           }}
        />
      </div>
    );
}
复制代码

 

 

---------------

All 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;

/*PC*/
const Todo = ({
  text,
  completed,
  onTodoClick
})=>{
  return (
    <li 
                onClick={onTodoClick}
                style={{
                  textDecoration:
                    completed ?
                      'line-through' :
                      'none'
                }}>
              {text}
            </li>
  );
}


/*PC*/
const TodoList = ({
  todos,
  onTodoClick
}) => {
  return (
    <ul>
          {todos.map(todo =>
            <Todo 
             key={todo.id}
             {...todo}
             onClick={
                ()=>{
                  onTodoClick
                }
             } 
           />
          )}
        </ul>
  );
}

/**
 Functional compoment, persental compoment: doesn't need to know what to do, just show the interface, call the callback function.
*/
const AddTodo = ({
  onAddTodo
}) => {
  
  let input;
  return (
     <div>
          <input ref={node => {
          input = node;
        }} />
        <button onClick={() => {
          onAddTodo(input.value);
          input.value = '';
        }}>
          Add Todo
        </button>
     </div>
  );
}

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

const FilterLink = ({
  filter,
  currentFilter,
  children,
  onFilterClick
}) => {
  if (filter === currentFilter) {
    return <span>{children}</span>;
  }

  return (
    <a href='#'
       onClick={e => {
         e.preventDefault();
         onFilterClick(filter);
       }}
    >
      {children}
    </a>
  );
};

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
}) => {
  return (
      <div>
        <AddTodo 
           onAddTodo={ text =>
              store.dispatch({
                  type: 'ADD_TODO',
                  id: nextTodoId++,
                  text
              })
           }
        />
        <TodoList 
          todos={getVisibleTodos(
              todos,
              visibilityFilter
          )}
          onTodoClick={
            (id)=>{
              store.dispatch({
                type: 'TOGGLE_TODO',
                id
              })
            }
          }
        />
        <Footer 
           visibilityFilter = {visibilityFilter}
           onFilterClick={ (filter) => {
              store.dispatch({
                 type: 'SET_VISIBILITY_FILTER',
                 filter
             });
           }}
        />
      </div>
    );
}


const render = () => {
  ReactDOM.render(
    <TodoApp
      {...store.getState()}
    />,
    document.getElementById('root')
  );
};

store.subscribe(render);
render();
复制代码

 

posted @   Zhentiw  阅读(192)  评论(0编辑  收藏  举报
(评论功能已被禁用)
编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示