• 用redux完成事务清单


    今天再来一个例子,我们从组件开始。

    App.js

     1 import React, { PropTypes } from 'react'
     2 import { bindActionCreators } from 'redux'
     3 import { connect } from 'react-redux'
     4 import Header from '../components/Header'
     5 import MainSection from '../components/MainSection'
     6 import * as TodoActions from '../actions'
     7 
     8 const App = ({todos, actions}) => (
     9   <div>
    10     <Header addTodo={actions.addTodo} /> //头部负责添加事项
    11     <MainSection todos={todos} actions={actions} />  //这是2个组件
    12   </div>
    13 )
    14 
    15 App.propTypes = {
    16   todos: PropTypes.array.isRequired,
    17   actions: PropTypes.object.isRequired  //todos是数组  actions是对象
    18 }
    19 
    20 const mapStateToProps = state => ({
    21   todos: state.todos
    22 })
    23 
    24 const mapDispatchToProps = dispatch => ({
    25     actions: bindActionCreators(TodoActions, dispatch)
    26 })
    27 
    28 export default connect(
    29   mapStateToProps,
    30   mapDispatchToProps
    31 )(App)

    head.js  

     1 import React, { PropTypes, Component } from 'react'
     2 import TodoTextInput from './TodoTextInput'
     3 
     4 export default class Header extends Component {  //es6组建写法
     5   static propTypes = {
     6     addTodo: PropTypes.func.isRequired
     7   }
     8 
     9   handleSave = text => {
    10     if (text.length !== 0) {
    11       this.props.addTodo(text)
    12     }
    13   }
    14 
    15   render() {
    16     return (
    17       <header className="header">
    18         <h1>todos</h1>
    19         <TodoTextInput newTodo
    20                        onSave={this.handleSave} //添加事项
    21                        placeholder="What needs to be done?" />
    22       </header>
    23     )
    24   }
    25 }
    TodoTextInput.js
     1 import React, { Component, PropTypes } from 'react'
     2 import classnames from 'classnames'
     3 
     4 export default class TodoTextInput extends Component {
     5   static propTypes = {
     6     onSave: PropTypes.func.isRequired,
     7     text: PropTypes.string,
     8     placeholder: PropTypes.string,
     9     editing: PropTypes.bool,
    10     newTodo: PropTypes.bool
    11   }
    12 
    13   state = {
    14     text: this.props.text || ''
    15   }
    16 
    17   handleSubmit = e => {
    18     const text = e.target.value.trim()
    19     if (e.which === 13) {
    20       this.props.onSave(text)  //保存文本值
    21       if (this.props.newTodo) {
    22         this.setState({ text: '' })
    23       }
    24     }
    25   }
    26 
    27   handleChange = e => {
    28     this.setState({ text: e.target.value })
    29   }
    30 
    31   handleBlur = e => {
    32     if (!this.props.newTodo) {
    33       this.props.onSave(e.target.value)
    34     }
    35   }
    36 
    37   render() {
    38     return (
    39       <input className={
    40         classnames({
    41           edit: this.props.editing,
    42           'new-todo': this.props.newTodo
    43         })}
    44         type="text"
    45         placeholder={this.props.placeholder} //what needs to be done?
    46         autoFocus="true"
    47         value={this.state.text}
    48         onBlur={this.handleBlur}
    49         onChange={this.handleChange}  //改变state里面的值
    50         onKeyDown={this.handleSubmit} /> //keycode=13 提交保存
    51     )
    52   }
    53 }

    mainSection.js

     1 import React, { Component, PropTypes } from 'react'
     2 import TodoItem from './TodoItem'
     3 import Footer from './Footer'
     4 import { SHOW_ALL, SHOW_COMPLETED, SHOW_ACTIVE } from '../constants/TodoFilters' //过滤器
     5 
     6 const TODO_FILTERS = {
     7   [SHOW_ALL]: () => true,
     8   [SHOW_ACTIVE]: todo => !todo.completed,
     9   [SHOW_COMPLETED]: todo => todo.completed
    10 }
    11 
    12 export default class MainSection extends Component {
    13   static propTypes = {
    14     todos: PropTypes.array.isRequired,
    15     actions: PropTypes.object.isRequired
    16   }
    17 
    18   state = { filter: SHOW_ALL }
    19 
    20   handleClearCompleted = () => {
    21     this.props.actions.clearCompleted() //清除完成事项
    22   }
    23 
    24   handleShow = filter => {
    25     this.setState({ filter })  //根据过滤条件展示
    26   }
    27 
    28   renderToggleAll(completedCount) {
    29     const { todos, actions } = this.props
    30     if (todos.length > 0) {
    31       return (
    32         <input className="toggle-all"
    33                type="checkbox"
    34                checked={completedCount === todos.length} //是否全部完成
    35                onChange={actions.completeAll} />
    36       )
    37     }
    38   }
    39 
    40   renderFooter(completedCount) {
    41     const { todos } = this.props
    42     const { filter } = this.state
    43     const activeCount = todos.length - completedCount
    44 
    45     if (todos.length) {
    46       return (
    47         <Footer completedCount={completedCount}
    48                 activeCount={activeCount}
    49                 filter={filter}
    50                 onClearCompleted={this.handleClearCompleted.bind(this)}
    51                 onShow={this.handleShow.bind(this)} />
    52       )
    53     }
    54   }
    55 
    56   render() {
    57     const { todos, actions } = this.props
    58     const { filter } = this.state
    59 
    60     const filteredTodos = todos.filter(TODO_FILTERS[filter])  //初始值为showall 展示全部
    61     const completedCount = todos.reduce((count, todo) =>   //统计已完成事项
    62       todo.completed ? count + 1 : count,
    63       0
    64     )
    65 
    66     return (
    67       <section className="main">
    68         {this.renderToggleAll(completedCount)}
    69         <ul className="todo-list">
    70           {filteredTodos.map(todo =>
    71             <TodoItem key={todo.id} todo={todo} {...actions} />
    72           )}
    73         </ul>
    74         {this.renderFooter(completedCount)}
    75       </section>
    76     )
    77   }
    78 }

    todoItems.js

     1 import React, { Component, PropTypes } from 'react'
     2 import classnames from 'classnames'
     3 import TodoTextInput from './TodoTextInput'
     4 
     5 export default class TodoItem extends Component {
     6   static propTypes = {
     7     todo: PropTypes.object.isRequired,
     8     editTodo: PropTypes.func.isRequired,
     9     deleteTodo: PropTypes.func.isRequired,
    10     completeTodo: PropTypes.func.isRequired
    11   }
    12 
    13   state = {
    14     editing: false
    15   }
    16 
    17   handleDoubleClick = () => {
    18     this.setState({ editing: true })
    19   }
    20 
    21   handleSave = (id, text) => {
    22     if (text.length === 0) {
    23       this.props.deleteTodo(id)
    24     } else {
    25       this.props.editTodo(id, text)   //编辑  不存在text则删除该项
    26     }
    27     this.setState({ editing: false })
    28   }
    29 
    30   render() {
    31     const { todo, completeTodo, deleteTodo } = this.props
    32 
    33     let element
    34     if (this.state.editing) {
    35       element = (
    36         <TodoTextInput text={todo.text}
    37                        editing={this.state.editing}
    38                        onSave={(text) => this.handleSave(todo.id, text)} />
    39       )
    40     } else {
    41       element = (
    42         <div className="view">
    43           <input className="toggle"
    44                  type="checkbox"
    45                  checked={todo.completed}
    46                  onChange={() => completeTodo(todo.id)} />
    47           <label onDoubleClick={this.handleDoubleClick}>  //双击修改为可编辑状态   会重新渲染
    48             {todo.text}
    49           </label>
    50           <button className="destroy"
    51                   onClick={() => deleteTodo(todo.id)} />
    52         </div>
    53       )
    54     }
    55 
    56     return (
    57       <li className={classnames({
    58         completed: todo.completed,
    59         editing: this.state.editing
    60       })}>
    61         {element}
    62       </li>
    63     )
    64   }
    65 }

    footer.js

     1 import React, { PropTypes, Component } from 'react'
     2 import classnames from 'classnames'
     3 import { SHOW_ALL, SHOW_COMPLETED, SHOW_ACTIVE } from '../constants/TodoFilters'
     4 
     5 const FILTER_TITLES = {
     6   [SHOW_ALL]: 'All',
     7   [SHOW_ACTIVE]: 'Active',
     8   [SHOW_COMPLETED]: 'Completed'
     9 }
    10 
    11 export default class Footer extends Component {
    12   static propTypes = {
    13     completedCount: PropTypes.number.isRequired,
    14     activeCount: PropTypes.number.isRequired,
    15     filter: PropTypes.string.isRequired,
    16     onClearCompleted: PropTypes.func.isRequired,
    17     onShow: PropTypes.func.isRequired
    18   }
    19 
    20   renderTodoCount() {
    21     const { activeCount } = this.props
    22     const itemWord = activeCount === 1 ? 'item' : 'items'  //此处语法经典
    23 
    24     return (
    25       <span className="todo-count">
    26         <strong>{activeCount || 'No'}</strong> {itemWord} left  //有多少未完成事项
    27       </span>
    28     )
    29   }
    30 
    31   renderFilterLink(filter) {
    32     const title = FILTER_TITLES[filter]
    33     const { filter: selectedFilter, onShow } = this.props
    34 
    35     return (
    36       <a className={classnames({ selected: filter === selectedFilter })}
    37          style={{ cursor: 'pointer' }}
    38          onClick={() => onShow(filter)}>
    39         {title}
    40       </a>
    41     )
    42   }
    43 
    44   renderClearButton() {
    45     const { completedCount, onClearCompleted } = this.props
    46     if (completedCount > 0) {
    47       return (
    48         <button className="clear-completed"
    49                 onClick={onClearCompleted} >
    50           Clear completed
    51         </button>
    52       )
    53     }
    54   }
    55 
    56   render() {
    57     return (
    58       <footer className="footer">
    59         {this.renderTodoCount()}
    60         <ul className="filters">
    61           {[ SHOW_ALL, SHOW_ACTIVE, SHOW_COMPLETED ].map(filter =>
    62             <li key={filter}>
    63               {this.renderFilterLink(filter)}   //切换不同的过滤条件
    64             </li>
    65           )}
    66         </ul>
    67         {this.renderClearButton()}
    68       </footer>
    69     )
    70   }
    71 }

    好了  这就是组件  比上次有点多  不过当成是训练    最重要的是把握整体结构

    indes.js

    1 import * as types from '../constants/ActionTypes'
    2 
    3 export const addTodo = text => ({ type: types.ADD_TODO, text })
    4 export const deleteTodo = id => ({ type: types.DELETE_TODO, id })
    5 export const editTodo = (id, text) => ({ type: types.EDIT_TODO, id, text })  //这一看就是dispatch的内容
    6 export const completeTodo = id => ({ type: types.COMPLETE_TODO, id })
    7 export const completeAll = () => ({ type: types.COMPLETE_ALL })
    8 export const clearCompleted = () => ({ type: types.CLEAR_COMPLETED })

    todo.js

     1 import { ADD_TODO, DELETE_TODO, EDIT_TODO, COMPLETE_TODO, COMPLETE_ALL, CLEAR_COMPLETED } from '../constants/ActionTypes'
     2 
     3 const initialState = [
     4   {
     5     text: 'Use Redux',
     6     completed: false,
     7     id: 0               //初始化state
     8   }
     9 ]
    10 
    11 export default function todos(state = initialState, action) {
    12   switch (action.type) {
    13     case ADD_TODO:
    14       return [
    15         {
    16           id: state.reduce((maxId, todo) => Math.max(todo.id, maxId), -1) + 1, //确定id值
    17           completed: false,
    18           text: action.text
    19         },
    20         ...state
    21       ]
    22 
    23     case DELETE_TODO:
    24       return state.filter(todo =>
    25         todo.id !== action.id
    26       )
    27 
    28     case EDIT_TODO:
    29       return state.map(todo =>
    30         todo.id === action.id?
    31         (...todo,{text:action.text}):
    32          todo
    33       )
    34 
    35     case COMPLETE_TODO:
    36       return state.map(todo =>
    37         todo.id === action.id ?
    38           { ...todo, completed: !todo.completed } : //用于切换
    39           todo
    40       )
    41 
    42     case COMPLETE_ALL:
    43       const areAllMarked = state.every(todo => todo.completed) //全部完成 或全部未完成
    44       return state.map(todo => ({
    45         ...todo,
    46         completed: !areAllMarked
    47       }))
    48 
    49     case CLEAR_COMPLETED:
    50       return state.filter(todo => todo.completed === false)
    51 
    52     default:
    53       return state
    54   }
    55 }

    index.js  整合reducers

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

    好了  代码就这么多  这一次reducers倒不是很复杂  反倒组件有点乱   总之是单向数据流动,根据action的类型做出相应的改变,最后重新render。。。

     
  • 相关阅读:
    基于jQuery弹性展开收缩菜单插件gooey.js
    动态插入和移除
    匹配0-59 0-23 的正则表达式
    换行问题
    iOSBug -- The file “Info.plist” couldn’t be opened
    MarkDown语法
    Mac -- 右键添加pages文件
    iOS -- 使用蒲公英进行内测
    Mac使用技巧--如何给safari安装翻译插件
    iOS--优化
  • 原文地址:https://www.cnblogs.com/yoissee/p/5968175.html
Copyright © 2020-2023  润新知