- 示例: Todo 列表
- 入口文件
index.js
- 入口文件
- 创建 Action
actions/index.js
- Reducers
reducers/todos.jsreducers/visibilityFilter.jsreducers/index.js
- 展示组件
components/Todo.jscomponents/TodoList.jscomponents/Link.jscomponents/Footer.jscomponents/App.js
- 容器组件
containers/VisibleTodoList.jscontainers/FilterLink.js
- 其他组件
containers/AddTodo.js
示例: Todo 列表
这是我们在基础教程里开发的迷你型的任务管理应用的完整源码。
入口文件
index.js
import React from 'react'import { render } from 'react-dom'import { Provider } from 'react-redux'import { createStore } from 'redux'import todoApp from './reducers'import App from './components/App'let store = createStore(todoApp)render(<Provider store={store}><App /></Provider>,document.getElementById('root'))
创建 Action
actions/index.js
let nextTodoId = 0export const addTodo = text => {return {type: 'ADD_TODO',id: nextTodoId++,text}}export const setVisibilityFilter = filter => {return {type: 'SET_VISIBILITY_FILTER',filter}}export const toggleTodo = id => {return {type: 'TOGGLE_TODO',id}}
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.filterdefault:return state}}export default visibilityFilter
reducers/index.js
import { combineReducers } from 'redux'import todos from './todos'import visibilityFilter from './visibilityFilter'const todoApp = combineReducers({todos,visibilityFilter})export default todoApp
展示组件
components/Todo.js
import React from 'react'import PropTypes from 'prop-types'const Todo = ({ onClick, completed, text }) => (<lionClick={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, onTodoClick }) => (<ul>{todos.map(todo => (<Todo key={todo.id} {...todo} onClick={() => onTodoClick(todo.id)} />))}</ul>)TodoList.propTypes = {todos: PropTypes.arrayOf(PropTypes.shape({id: PropTypes.number.isRequired,completed: PropTypes.bool.isRequired,text: PropTypes.string.isRequired}).isRequired).isRequired,onTodoClick: PropTypes.func.isRequired}export default TodoList
components/Link.js
import React from 'react'import PropTypes from 'prop-types'const Link = ({ active, children, onClick }) => {if (active) {return <span>{children}</span>}return (<ahref=""onClick={e => {e.preventDefault()onClick()}}>{children}</a>)}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'const Footer = () => (<p>Show:{' '}<FilterLink filter="SHOW_ALL">All</FilterLink>{', '}<FilterLink filter="SHOW_ACTIVE">Active</FilterLink>{', '}<FilterLink filter="SHOW_COMPLETED">Completed</FilterLink></p>)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 => {return {todos: getVisibleTodos(state.todos, state.visibilityFilter)}}const mapDispatchToProps = dispatch => {return {onTodoClick: id => {dispatch(toggleTodo(id))}}}const VisibleTodoList = connect(mapStateToProps,mapDispatchToProps)(TodoList)export default VisibleTodoList
containers/FilterLink.js
import { connect } from 'react-redux'import { setVisibilityFilter } from '../actions'import Link from '../components/Link'const mapStateToProps = (state, ownProps) => {return {active: ownProps.filter === state.visibilityFilter}}const mapDispatchToProps = (dispatch, ownProps) => {return {onClick: () => {dispatch(setVisibilityFilter(ownProps.filter))}}}const FilterLink = connect(mapStateToProps,mapDispatchToProps)(Link)export default FilterLink
其他组件
containers/AddTodo.js
import React from 'react'import { connect } from 'react-redux'import { addTodo } from '../actions'let AddTodo = ({ dispatch }) => {let inputreturn (<div><formonSubmit={e => {e.preventDefault()if (!input.value.trim()) {return}dispatch(addTodo(input.value))input.value = ''}}><inputref={node => {input = node}}/><button type="submit">Add Todo</button></form></div>)}AddTodo = connect()(AddTodo)export default AddTodo
