React 快速上手 - 08 redux 状态管理 react-redux

redux

本系列目录

点赞是美德 : )前端


今天我会快速的过一下 redux 知识,而后讨论下 适用性,最后为了让你们能快速学习,简化了官方的经典 Demo todo ,跟着我作一遍就行。node

目标

  • 告终什么是 redux
  • 什么状况下适用
  • 建立一个 redux 应用
  • 安装调试插件

闲话

redux 是一个很深的话题,先聊聊标准学习路线,打开 redux.js.org/introductio…react

笔者为了写这文章,但是安静的读了一遍git

首先 Introduction 7 篇github

redux-introduction

好像懂了什么,不知道呢web

而后 Base 6 篇chrome

redux-Base

其实 Example: Todo List 这才是最有帮助的redux

又听了视频 Getting Started with Redux 视频segmentfault

好啰嗦啊api

Redux 视频 Getting Started with Redux

不过这也是学习的正确姿式,总要看一遍官方文档,无奈官网设计的更像一个学术站点,而不是类库工具一类的指导说明

好了好了~吐槽完毕,开始正文!

redux 是什么, 能够不用么

ducafecat_2018-05-25-11-27-37

这张图是 Flux 官网的,画的很好。

Redux 是一种 数据的管理 方式,界面上发起各类操做 Action ,而后 DispatcherStore 更新状态 State,推送新状态到视图 View

好了,概念一句话说完了,来看看什么状况下用 Redux

斗鱼界面

这个界面,若是用 React 来实现,在底部的 容器组件 要处理的业务有: 用户登陆、弹幕、主播信息、视频进度、道具、打赏、IM聊天、等等还有不少 并且随着产品迭代,功能只会多

按咱们以前的组件拆分,结构是合理,可是这个数据管理麻烦了,各类业务数据压入子组件,各类业务事件返回到主容器组件,可能的代码结构以下

// 状态
this.state = {
  data1:...,
  data2:...,
  data3:...,
  data...n:...
}

// 事件
function handelEvent1 = {...}
function handelEvent2 = {...}
function handelEvent3 = {...}
function handelEvent...n = {...}

// JSX
<主视图组件>
  <用户信息 data1={this.state.data1} handleEven1={...} handleEven2={...} handleEven...n={...} /> <主播信息> <基础资料 data...n={...} handleEven...n={...} /> <头像 data...n={...} handleEven...n={... /> <关注 data...n={...} handleEven...n={... /> <标签 data...n={...} handleEven...n={... /> <热度 data...n={...} handleEven...n={... /> ... </主播信息> <播放器 ...> <... /> ... <播放器> ... <...播放器/> ... </主视图组件> 复制代码

会发现组件套组件,父父子子的,彻底无法维护了,梳理这些关系就很费时间,并且容易错误

Redux 就是来解决这个问题的,每一个组件只要执行本身的 Action ,不用返回到父容器

  • 好比 Redux 在弹幕业务中就两步:
    1. 弹幕发出组件 执行发出弹幕动做 Action 内容 { type: 'BARRAGE_SEND', text: '弹幕消息' }
    2. 弹幕滚动组件 新弹幕数据被更新到弹幕滚动组件

可是也很差滥用,我看到有些简单的 表单操做,居然也套了 Redux ,彻底不必,本身把握吧

动手建立一个 redux 应用

来个经典例子 todo , 原版 Todo , 我这里是精简版,那么咱们开始

  • 动图效果

react-redux-todo

  • 组件结构

todo 组件结构

第一步: 编写 Action

界面上产生的操做

let nextTodoId = 0

export const addTodo = text => ({
  type: 'ADD_TODO',
  id: nextTodoId++,
  text
})

export const toggleTodo = id => ({
  type: 'TOGGLE_TODO',
  id
})
复制代码
  • 事件的格式
    • type 字段必须有,表示作什么操做
    • type 值全局惟一
    • type 大写定义
    • 其它字段自由定义

第二步: 编写 Reducers

事件对应的响应处理,处理完后返回新 state

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

使用 combineReducers 合并全部的处理过程

import { combineReducers } from 'redux'
import todos from './todos'

export default combineReducers({
  todos
})
复制代码

假设你有其它业务, 如: 用户 user , 购物车 cart

import { combineReducers } from 'redux'
import todos from './todos'
import user from './user'
import cart from './cart'

export default combineReducers({
  todos,
  user,
  cart
})
复制代码

第四步: 编写组件 AddTodo

  • 使用 connect 链接组件
  • 使用 dispatch 方法派发事件
import React from 'react'
import { connect } from 'react-redux'
import { addTodo } from '../redux/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) 复制代码

dispatch(addTodo(input.value)) 就发动了 ReduxReducers , 这时 state 更新了

第五步: 编写组件 TodoList

再来个稍微复杂的

import React from 'react'
import {connect} from 'react-redux'
import {toggleTodo} from '../redux/actions'

const Todo = ({onClick, completed, text}) => (
  <li onClick={onClick} style={{ textDecoration: completed ? 'line-through' : 'none' }} > {text} </li>
)

const TodoList = ({todos, toggleTodo}) => (
  <ul> {todos.map(todo => ( <Todo key={todo.id} {...todo} onClick={() => toggleTodo(todo.id)} /> ))} </ul> ) const mapStateToProps = state => ({ todos: state.todos }) const mapDispatchToProps = dispatch => ({ toggleTodo: id => dispatch(toggleTodo(id)) }) export default connect(mapStateToProps, mapDispatchToProps)(TodoList) 复制代码

仍是 connect 方法 , 这里重点讲下

先看看官方定义 connect()

  • 定义
connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])
复制代码
  • 参数
名称 说明
mapStateToProps store 绑定 state , 作更新的须要传入
mapDispatchToProps 绑定派发事件 event , 不传的话默认 dispatch 对象, 就像上面的 AddTodo 组件
mergeProps [mergeProps(stateProps, dispatchProps, ownProps): props] (Function): 合并属性自定义,本身不传的话默认 ·Object.assign ,我也是放空默认的
options 一些选项,我没怎么在乎,知道有就行

[mapStateToProps], [mapDispatchToProps] 这两个用到的多,你们本身练习下

第六步: 容器组件

import React from 'react'
import AddTodo from './AddTodo'
import TodoList from './TodoList'

const App = () => (
  <div> <AddTodo /> <TodoList /> </div>
)

export default App
复制代码

这里简单,并列排放

最后: 适配 App

import React, {Component} from 'react'
import {createStore} from 'redux'
import {Provider} from 'react-redux'
import TodoApp from './todos/components/App'
import todoReducer from './todos/redux/reducers'

const store = createStore(todoReducer)

class BaseRedux extends Component {
  render() {
    return (
      <Provider store={store}> <TodoApp /> </Provider>
    )
  }
}

export default BaseRedux
复制代码
  • 标准代码格式
    1. createStore(reducers) 建立 store
    2. Provider 适配器压入 store 对象, 子节点都受 Redux 控制

调试工具 Redux DevTools extension

动图效果

Redux DevTools extension

1. 安装 chrome 插件

打开 redux-devtools

redux-devtools

2. 配置代码

const store = createStore(
  todoReducer,
  window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
)
复制代码

createStore 时加入 window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()

3. 开启插件

打开 chrome 调试工具,点击面板 Redux

chrome-tab-Redux

codepen

https://codepen.io/ducafecat/pen/RyBEeK

代码

参考

相关文章
相关标签/搜索