• 示例: Todo 列表
    • 入口文件
      • index.js
  • 创建 Action
    • actions/index.js
  • Reducers
    • reducers/todos.js
    • reducers/visibilityFilter.js
    • reducers/index.js
  • 展示组件
    • components/Todo.js
    • components/TodoList.js
    • components/Link.js
    • components/Footer.js
    • components/App.js
  • 容器组件
    • containers/VisibleTodoList.js
    • containers/FilterLink.js
  • 其他组件
    • containers/AddTodo.js

    示例: Todo 列表

    这是我们在基础教程里开发的迷你型的任务管理应用的完整源码。

    入口文件

    index.js

    1. import React from 'react'
    2. import { render } from 'react-dom'
    3. import { Provider } from 'react-redux'
    4. import { createStore } from 'redux'
    5. import todoApp from './reducers'
    6. import App from './components/App'
    7. let store = createStore(todoApp)
    8. render(
    9. <Provider store={store}>
    10. <App />
    11. </Provider>,
    12. document.getElementById('root')
    13. )

    创建 Action

    actions/index.js

    1. let nextTodoId = 0
    2. export const addTodo = text => {
    3. return {
    4. type: 'ADD_TODO',
    5. id: nextTodoId++,
    6. text
    7. }
    8. }
    9. export const setVisibilityFilter = filter => {
    10. return {
    11. type: 'SET_VISIBILITY_FILTER',
    12. filter
    13. }
    14. }
    15. export const toggleTodo = id => {
    16. return {
    17. type: 'TOGGLE_TODO',
    18. id
    19. }
    20. }

    Reducers

    reducers/todos.js

    1. const todos = (state = [], action) => {
    2. switch (action.type) {
    3. case 'ADD_TODO':
    4. return [
    5. ...state,
    6. {
    7. id: action.id,
    8. text: action.text,
    9. completed: false
    10. }
    11. ]
    12. case 'TOGGLE_TODO':
    13. return state.map(todo =>
    14. (todo.id === action.id)
    15. ? {...todo, completed: !todo.completed}
    16. : todo
    17. )
    18. default:
    19. return state
    20. }
    21. }
    22. export default todos

    reducers/visibilityFilter.js

    1. const visibilityFilter = (state = 'SHOW_ALL', action) => {
    2. switch (action.type) {
    3. case 'SET_VISIBILITY_FILTER':
    4. return action.filter
    5. default:
    6. return state
    7. }
    8. }
    9. export default visibilityFilter

    reducers/index.js

    1. import { combineReducers } from 'redux'
    2. import todos from './todos'
    3. import visibilityFilter from './visibilityFilter'
    4. const todoApp = combineReducers({
    5. todos,
    6. visibilityFilter
    7. })
    8. export default todoApp

    展示组件

    components/Todo.js

    1. import React from 'react'
    2. import PropTypes from 'prop-types'
    3. const Todo = ({ onClick, completed, text }) => (
    4. <li
    5. onClick={onClick}
    6. style={ {
    7. textDecoration: completed ? 'line-through' : 'none'
    8. }}
    9. >
    10. {text}
    11. </li>
    12. )
    13. Todo.propTypes = {
    14. onClick: PropTypes.func.isRequired,
    15. completed: PropTypes.bool.isRequired,
    16. text: PropTypes.string.isRequired
    17. }
    18. export default Todo

    components/TodoList.js

    1. import React from 'react'
    2. import PropTypes from 'prop-types'
    3. import Todo from './Todo'
    4. const TodoList = ({ todos, onTodoClick }) => (
    5. <ul>
    6. {todos.map(todo => (
    7. <Todo key={todo.id} {...todo} onClick={() => onTodoClick(todo.id)} />
    8. ))}
    9. </ul>
    10. )
    11. TodoList.propTypes = {
    12. todos: PropTypes.arrayOf(
    13. PropTypes.shape({
    14. id: PropTypes.number.isRequired,
    15. completed: PropTypes.bool.isRequired,
    16. text: PropTypes.string.isRequired
    17. }).isRequired
    18. ).isRequired,
    19. onTodoClick: PropTypes.func.isRequired
    20. }
    21. export default TodoList
    1. import React from 'react'
    2. import PropTypes from 'prop-types'
    3. const Link = ({ active, children, onClick }) => {
    4. if (active) {
    5. return <span>{children}</span>
    6. }
    7. return (
    8. <a
    9. href=""
    10. onClick={e => {
    11. e.preventDefault()
    12. onClick()
    13. }}
    14. >
    15. {children}
    16. </a>
    17. )
    18. }
    19. Link.propTypes = {
    20. active: PropTypes.bool.isRequired,
    21. children: PropTypes.node.isRequired,
    22. onClick: PropTypes.func.isRequired
    23. }
    24. export default Link
    1. import React from 'react'
    2. import FilterLink from '../containers/FilterLink'
    3. const Footer = () => (
    4. <p>
    5. Show:
    6. {' '}
    7. <FilterLink filter="SHOW_ALL">
    8. All
    9. </FilterLink>
    10. {', '}
    11. <FilterLink filter="SHOW_ACTIVE">
    12. Active
    13. </FilterLink>
    14. {', '}
    15. <FilterLink filter="SHOW_COMPLETED">
    16. Completed
    17. </FilterLink>
    18. </p>
    19. )
    20. export default Footer

    components/App.js

    1. import React from 'react'
    2. import Footer from './Footer'
    3. import AddTodo from '../containers/AddTodo'
    4. import VisibleTodoList from '../containers/VisibleTodoList'
    5. const App = () => (
    6. <div>
    7. <AddTodo />
    8. <VisibleTodoList />
    9. <Footer />
    10. </div>
    11. )
    12. export default App

    容器组件

    containers/VisibleTodoList.js

    1. import { connect } from 'react-redux'
    2. import { toggleTodo } from '../actions'
    3. import TodoList from '../components/TodoList'
    4. const getVisibleTodos = (todos, filter) => {
    5. switch (filter) {
    6. case 'SHOW_COMPLETED':
    7. return todos.filter(t => t.completed)
    8. case 'SHOW_ACTIVE':
    9. return todos.filter(t => !t.completed)
    10. case 'SHOW_ALL':
    11. default:
    12. return todos
    13. }
    14. }
    15. const mapStateToProps = state => {
    16. return {
    17. todos: getVisibleTodos(state.todos, state.visibilityFilter)
    18. }
    19. }
    20. const mapDispatchToProps = dispatch => {
    21. return {
    22. onTodoClick: id => {
    23. dispatch(toggleTodo(id))
    24. }
    25. }
    26. }
    27. const VisibleTodoList = connect(
    28. mapStateToProps,
    29. mapDispatchToProps
    30. )(TodoList)
    31. export default VisibleTodoList
    1. import { connect } from 'react-redux'
    2. import { setVisibilityFilter } from '../actions'
    3. import Link from '../components/Link'
    4. const mapStateToProps = (state, ownProps) => {
    5. return {
    6. active: ownProps.filter === state.visibilityFilter
    7. }
    8. }
    9. const mapDispatchToProps = (dispatch, ownProps) => {
    10. return {
    11. onClick: () => {
    12. dispatch(setVisibilityFilter(ownProps.filter))
    13. }
    14. }
    15. }
    16. const FilterLink = connect(
    17. mapStateToProps,
    18. mapDispatchToProps
    19. )(Link)
    20. export default FilterLink

    其他组件

    containers/AddTodo.js

    1. import React from 'react'
    2. import { connect } from 'react-redux'
    3. import { addTodo } from '../actions'
    4. let AddTodo = ({ dispatch }) => {
    5. let input
    6. return (
    7. <div>
    8. <form
    9. onSubmit={e => {
    10. e.preventDefault()
    11. if (!input.value.trim()) {
    12. return
    13. }
    14. dispatch(addTodo(input.value))
    15. input.value = ''
    16. }}
    17. >
    18. <input
    19. ref={node => {
    20. input = node
    21. }}
    22. />
    23. <button type="submit">
    24. Add Todo
    25. </button>
    26. </form>
    27. </div>
    28. )
    29. }
    30. AddTodo = connect()(AddTodo)
    31. export default AddTodo