多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
# 示例: Todo 列表 这是我们在[基础教程](./README.md)里开发的迷你型的任务管理应用的完整源码。这个代码也在[我们的 example 仓库](https://github.com/reactjs/redux/tree/master/examples/todos/src)可以找到。 当然也可以[在浏览器通过 CodeSandbox 运行](https://codesandbox.io/s/github/reactjs/redux/tree/master/examples/todos). ## 入口文件 #### `index.js` ```js import React from 'react' import { render } from 'react-dom' import { Provider } from 'react-redux' import { createStore } from 'redux' import rootReducer from './reducers' import App from './components/App' const store = createStore(rootReducer) render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') ) ``` ## 创建 Action #### `actions/index.js` ```js let nextTodoId = 0 export const addTodo = text => ({ type: 'ADD_TODO', id: nextTodoId++, text }) export const setVisibilityFilter = filter => ({ type: 'SET_VISIBILITY_FILTER', filter }) export const toggleTodo = id => ({ type: 'TOGGLE_TODO', id }) export const VisibilityFilters = { SHOW_ALL: 'SHOW_ALL', SHOW_COMPLETED: 'SHOW_COMPLETED', SHOW_ACTIVE: 'SHOW_ACTIVE' } ``` ## Reducers #### `reducers/todos.js` ```js const todos = (state = [], action) => { switch (action.type) { case 'ADD_TODO': return [ ...state, { id: action.id, text: action.text, completed: false } ] case 'TOGGLE_TODO': return state.map(todo => todo.id === action.id ? { ...todo, completed: !todo.completed } : todo ) default: return state } } export default todos ``` #### `reducers/visibilityFilter.js` ```js const visibilityFilter = (state = 'SHOW_ALL', action) => { switch (action.type) { case 'SET_VISIBILITY_FILTER': return action.filter default: return state } } export default visibilityFilter ``` #### `reducers/index.js` ```js import { combineReducers } from 'redux' import todos from './todos' import visibilityFilter from './visibilityFilter' export default combineReducers({ todos, visibilityFilter }) ``` ## 展示组件 #### `components/Todo.js` ```js import React from 'react' import PropTypes from 'prop-types' const Todo = ({ onClick, completed, text }) => ( <li onClick={onClick} style={{ textDecoration: completed ? 'line-through' : 'none' }} > {text} </li> ) Todo.propTypes = { onClick: PropTypes.func.isRequired, completed: PropTypes.bool.isRequired, text: PropTypes.string.isRequired } export default Todo ``` #### `components/TodoList.js` ```js import React from 'react' import PropTypes from 'prop-types' import Todo from './Todo' const TodoList = ({ todos, toggleTodo }) => ( <ul> {todos.map(todo => ( <Todo key={todo.id} {...todo} onClick={() => toggleTodo(todo.id)} /> ))} </ul> ) TodoList.propTypes = { todos: PropTypes.arrayOf( PropTypes.shape({ id: PropTypes.number.isRequired, completed: PropTypes.bool.isRequired, text: PropTypes.string.isRequired }).isRequired ).isRequired, toggleTodo: PropTypes.func.isRequired } export default TodoList ``` #### `components/Link.js` ```js import React from 'react' import PropTypes from 'prop-types' const Link = ({ active, children, onClick }) => ( <button onClick={onClick} disabled={active} style={{ marginLeft: '4px' }} > {children} </button> ) Link.propTypes = { active: PropTypes.bool.isRequired, children: PropTypes.node.isRequired, onClick: PropTypes.func.isRequired } export default Link ``` #### `components/Footer.js` ```js import React from 'react' import FilterLink from '../containers/FilterLink' import { VisibilityFilters } from '../actions' const Footer = () => ( <div> <span>Show: </span> <FilterLink filter={VisibilityFilters.SHOW_ALL}>All</FilterLink> <FilterLink filter={VisibilityFilters.SHOW_ACTIVE}>Active</FilterLink> <FilterLink filter={VisibilityFilters.SHOW_COMPLETED}>Completed</FilterLink> </div> ) export default Footer ``` #### `components/App.js` ```js import React from 'react' import Footer from './Footer' import AddTodo from '../containers/AddTodo' import VisibleTodoList from '../containers/VisibleTodoList' const App = () => ( <div> <AddTodo /> <VisibleTodoList /> <Footer /> </div> ) export default App ``` ## 容器组件 #### `containers/VisibleTodoList.js` ```js import { connect } from 'react-redux' import { toggleTodo } from '../actions' import TodoList from '../components/TodoList' const getVisibleTodos = (todos, filter) => { switch (filter) { case 'SHOW_COMPLETED': return todos.filter(t => t.completed) case 'SHOW_ACTIVE': return todos.filter(t => !t.completed) case 'SHOW_ALL': default: return todos } } const mapStateToProps = state => ({ todos: getVisibleTodos(state.todos, state.visibilityFilter) }) const mapDispatchToProps = dispatch => ({ toggleTodo: id => dispatch(toggleTodo(id)) }) export default connect( mapStateToProps, mapDispatchToProps )(TodoList) ``` #### `containers/FilterLink.js` ```js import { connect } from 'react-redux' import { setVisibilityFilter } from '../actions' import Link from '../components/Link' const mapStateToProps = (state, ownProps) => ({ active: ownProps.filter === state.visibilityFilter }) const mapDispatchToProps = (dispatch, ownProps) => ({ onClick: () => dispatch(setVisibilityFilter(ownProps.filter)) }) export default connect( mapStateToProps, mapDispatchToProps )(Link) ``` ### 其他组件 #### `containers/AddTodo.js` ```js import React from 'react' import { connect } from 'react-redux' import { addTodo } from '../actions' const AddTodo = ({ dispatch }) => { let input return ( <div> <form onSubmit={e => { e.preventDefault() if (!input.value.trim()) { return } dispatch(addTodo(input.value)) input.value = '' }} > <input ref={node => (input = node)} /> <button type="submit">Add Todo</button> </form> </div> ) } export default connect()(AddTodo) ```