Todo 列表示例
这是我们在基础教程里开发的迷你型的任务管理应用的完整源码。
这个代码也在我们的 example 仓库可以找到。 当然也可以在浏览器通过 CodeSandbox 运行.
入口文件
index.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
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
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
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
import { combineReducers } from 'redux' import todos from './todos' import visibilityFilter from './visibilityFilter' export default combineReducers({ todos, visibilityFilter })
展示组件
components/Todo.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
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
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
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
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
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
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
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)