专栏首页踏浪的文章redux_todoList案例

redux_todoList案例

上一节我们说到了redux的基础以及如何理解redux。这一节我们用经典的案例redux-todoList来具体的说一下每一个部分作何解释。

todoList是什么

todolist是一个经典的案例,代办项的意思。一般我们初学一门语言的时候基本都是会做一个todoList来验证自己所学的知识。我们这里用来理解redux也是一个不错的方法。

具体的功能可以查看http://www.todolist.cn/。一个输入框,输入代办事件,刚添加的归类到正在进行,我们可以点击具体的莫一项是他变成已完成。

我自己的代码以及托管到了github上面,之后的代码将会以我自己的代码为例,如果需要,可以去想clone。

梳理

我们回顾一下之前说redux的三要素:action,reducer,store。我们要完成todolist需要做一下什么呢?接下来看看具体的步骤。

入口

入口,即整个项目的入口文件。我们需要把我们需要的代码render(渲染到具体的某一个文件中)

import React, { PureComponent } from 'react'
import ReactDOM from 'react-dom'
import {Provider} from 'react-redux'
import {createStore} from 'redux'

import App from './src/index'
import reducer from './src/reducers'
require('./src/css/style.scss')

const store = createStore(reducer)

export default class Home extends PureComponent {
  render() {
    return <Provider store={store}>
      <App/>
    </Provider>
  }
}

ReactDOM.render(<Home/>, document.getElementById('root'))

首先是Provider组件,由react-reudx提供,作用就是作为整个项目的跟标签,我们把属性store传递到整个项目中去,供子组件使用。store就是由redux提供的createStore方法创建的。

我们说createStore方法接收一个参数,所有的reducer,那么reducer在哪里呢?接下来就是reducer。

reducer

我们说reducer是一个函数,给定一个确定的输入必定有一个确定的输出。他的作用就是操作我们需要的state(状态)。

那么在todolist里面有那几个状态呢?

  • 所有的代办项,我们用一个数组表示,即todos
  • todos的过滤,即我们当前所处一个状态,用visibilityFilter表示。

所以就有了以下的代码

import { combineReducers } from 'redux'
let initState = {
  todos: [],
  visibilityFilter: "SHOW_ACTIVE"
}

const todos = (state=initState.todos, action) => {
  switch (action.type) {
    case "ADD_TODO":
      // 千万不要使用push,返回的是数组的长度
      return [
          ...state,
          {
            text: action.text,
            complete: false,
            index: action.index
          }
        ]
    case "TOGGLE_TODO":
      return state.map((item, index) => {
        if( action.index == index ) {
          return Object.assign({}, item, {
            complete: !item.complete
          })
        }
        return item
      })
    default:
      return state
  }
}

const filterCompleteOrNot = (state=initState.visibilityFilter, action) => {
  switch (action.type) {
    case "FILTER_COMPLETE_OR_NOT":
      return action.filter
    default:
      return state
  }
}

const todoApp = combineReducers({
  todos,
  filterCompleteOrNot
})
export default todoApp

我们给定一个初始的状态initState,里面就有两个属性,todos与visibilityFilter,接下来就是编写纯函数了。

我们根据我们的状态来编写,todos会发生改变,什么情况下面会发生改变呢?

  • 添加todo
  • 修改todo的状态

所以我们的纯函数是这样写的

const todos = (state=initState.todos, action) => {
  switch (action.type) {
    case "ADD_TODO":
      // 千万不要使用push,返回的是数组的长度
      return [
          ...state,
          {
            text: action.text,
            complete: false,
            index: action.index
          }
        ]
    case "TOGGLE_TODO":
      return state.map((item, index) => {
        if( action.index == index ) {
          return Object.assign({}, item, {
            complete: !item.complete
          })
        }
        return item
      })
    default:
      return state
  }
}

更具我们操作的动作进行switch判断

接下来就是过滤todos,todos里面要显示那些东西呢

const filterCompleteOrNot = (state=initState.visibilityFilter, action) => {
  switch (action.type) {
    case "FILTER_COMPLETE_OR_NOT":
      return action.filter
    default:
      return state
  }
}

这就是我们的reducer。因为我们写了两个纯函数,但是在我们创建store的时候,createStore方法只接受一个参数,即所有纯函数的集合。所以我们需要使用redux提供的combineReducers方法把所有的reducer集合起来。

const todoApp = combineReducers({
  todos,
  filterCompleteOrNot
})

action

再来看看action,和上面的关联起来了

let initNumber = 0
const addTodo = (text) => ({
  text,
  type: "ADD_TODO",
  index: initNumber++
})

const toggleTodo = (index) => ({
  index,
  type: 'TOGGLE_TODO'
})

const filterCompleteOrNot = (filter) => ({
  filter,
  type: 'FILTER_COMPLETE_OR_NOT'
})

const TODOS_TYPE = {
  SHOW_ALL: "SHOW_ALL",
  SHOW_COMPLETE: "SHOW_COMPLETE",
  SHOW_ACTIVE: "SHOW_ACTIVE"
}

module.exports = {
  addTodo,
  toggleTodo,
  filterCompleteOrNot,
  TODOS_TYPE
}

我们知道action就是我们集体的操作,有哪一些呢?

  • 添加todo
  • 修改单个todo的state
  • 过滤todos

所以我们定义了三个action creater

const addTodo = (text) => ({
  text,
  type: "ADD_TODO",
  index: initNumber++
})

const toggleTodo = (index) => ({
  index,
  type: 'TOGGLE_TODO'
})

const filterCompleteOrNot = (filter) => ({
  filter,
  type: 'FILTER_COMPLETE_OR_NOT'
})

最后就是我们具体的展示层了

因为我们使用的是redux,而redux又提倡数据域结构分离,所以在文件目录下面有componentscontainers两个文件夹。但是普通的component与container没有什么关系,普通的组件里面没有store的dispatch等方法。所以我们在其中的几个文件代码中可以看到mapStateToPropsmapDispatchToProps两个方法。字面意思就是遍历state(dispatch)到props。意思就是把redux中的state与dispatch方法传递到props中(即组件component)。

const mapStateToProps = state => {
  return ({
    todos: getVisibleTodos(state.todos, state.filterCompleteOrNot)
  })
}

const mapDispatchToProps = dispatch => ({
  toggleTodo: index => dispatch(toggleTodo(index))
})

但是怎么传递呢?

在react-redux中,我们使用react-redux提供的connect方法。他的作用就是把component与container链接起来。

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(TodoList)

connect是一个二阶函数,第一次运行后返回一个函数再次运行。第一个运行的参数就是我们需要传递的两个props,第二次运行的参数就是我们需要传递到的组件。所以在TodoList文件中

import React from 'react'
import {
  ListGroup
} from 'react-bootstrap'

const TodoList = ({todos, toggleTodo}) => {
  return (
    <ListGroup>
      {
        todos.map((item, index) => <ListGroup.Item
          key={index}
          onClick={() => toggleTodo(item.index)}
          variant={item.complete ? 'success' : 'danger'}
        >
          <span className='name'>{item.text}</span>
          <span className='state'>{item.complete ? '已完成' : '未完成'}</span>
        </ListGroup.Item>)
      }
    </ListGroup>
  )
}

export default TodoList

我们才可以使用todos与toggleTodo,因为这两个参数都是我们使用mapStateToPropsmapDispatchToProps得到的。

其他的地方理解是一样的。

至于代码中用到的react-bootstrap,就是bootstrap封装的UI插件,集体可以看react-bootstrap

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • PM2常用命令

    pm2 start app.js -i 4 # cluster mode 模式启动4个app.js的应用实例 4个应用程序会自动进行负载均衡

    踏浪
  • HTML5表单

    number : 只能包含数字的输入框 color : 颜色选择器 datetime : 显示完整日期(chrome) datetime-local...

    踏浪
  • javascript函数

    JavaScript中的函数可以分为两类:有名函数与匿名函数。而定义函数的方式有两种:函数声明与函数表达式。

    踏浪
  • 简单易学的机器学习算法——梯度提升决策树GBDT

    梯度提升决策树(Gradient Boosting Decision Tree,GBDT)算法是近年来被提及比较多的一个算法,这主要得益于其算法的性能,以及该算...

    zhaozhiyong
  • 史上最严数据保护条例欧盟GDPR今日生效,你可能需要这版中文全文(上)

    大数据文摘
  • iOS 一款轻量级的AlertView

    系统有AlertController,如果设计师要求不高能满足需求了,但是如果设计师要单独设计一个对话框,在用AlertController就显得有点吃力了,自...

    赵哥窟
  • SQL Server 2016将支持R编程语言

    R编程语言最早出现于1993年,而在2000年,它的第一个符合产品质量的版本R-1.0发布了。自那之后,R就成为了统计分析方面的业界标准,围绕着这门语言出现了大...

    小莹莹
  • 基于OpenCV的图像分割处理!

    图像阈值化分割是一种传统的最常用的图像分割方法,因其实现简单、计算量小、性能较稳定而成为图像分割中最基本和应用最广泛的分割技术。它特别适用于目标和背景占据不同灰...

    Datawhale
  • Spy Banker木马新变种Telax利用谷歌云服务器进行传播

    安全公司Zscaler发现一种新型恶意活动,它基于一种新型的Spy Banker网银恶意软件Telax,利用谷歌云服务器来驻留木马下载器,且主要通过社交媒体平台...

    FB客服
  • 【死磕Java并发】-----J.U.C之阻塞队列:LinkedTransferQueue

    原文出处http://cmsblogs.com/ 『chenssy』 前面提到的各种BlockingQueue对读或者写都是锁上整个队列,在并发量大的时候,各种...

    用户1655470

扫码关注云+社区

领取腾讯云代金券