[Redux] Extracting Container Components -- VisibleTodoList
Code to be refacted:
const TodoList = ({ todos, onTodoClick }) => ( <ul> {todos.map(todo => <Todo key={todo.id} {...todo} onClick={() => onTodoClick(todo.id)} /> )} </ul> ); let nextTodoId = 0; const TodoApp = ({ todos, visibilityFilter }) => ( <div> ... ... <TodoList todos={ getVisibleTodos( todos, visibilityFilter ) } onTodoClick={id => store.dispatch({ type: 'TOGGLE_TODO', id }) } /> .... </div> );
Currently we pass the 'todos' and 'onTodoClick' from top container component 'TodoApp', all the way down to the 'Todo' component.
So we want simplfy 'TodoList' Component in TodoApp, create a new container component 'VisibleTodoList'.
In TodoApp:
const TodoApp = ({ todos, visibilityFilter }) => ( <div> <AddTodo onAddClick={text => store.dispatch({ type: 'ADD_TODO', id: nextTodoId++, text }) } /> <VisibleTodoList /> <Footer /> </div> );
VisibleTodoList:
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 }) } /> ); } }
----------------
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={() => { 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 ); } } 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 = ({ todos, visibilityFilter }) => ( <div> <AddTodo onAddClick={text => store.dispatch({ type: 'ADD_TODO', id: nextTodoId++, text }) } /> <VisibleTodoList /> <Footer /> </div> ); const render = () => { ReactDOM.render( <TodoApp {...store.getState()} />, document.getElementById('root') ); }; store.subscribe(render); render();
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 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工具