Redux理解

  1. redux 是一个独立专门用于做状态管理的JS 库(不是react 插件库)
  2. 它可以用在react, angular, vue 等项目中, 但基本与react 配合使用
  3. 作用: 集中式管理react 应用中多个组件共享的状态

image-20201115141633819

需要Redux时的情景

  • 某个组件的状态,需要共享
  • 某个状态需要在任何地方都可以拿到
  • 一个组件需要改变全局状态
  • 一个组件需要改变另一个组件的状态

Redux基本使用

  1. 安装

    1
    yarn add redux
  2. 为了更方便的管理store,因此新建一个文件夹store用于专门存放关于store相关操作的代码

    • store.js

      用于写创建stroe对象相关的代码

      1
      2
      3
      4
      5
      import { createStore } from 'redux'
      import { counter } from './reducers'
      // 生成一个store对象
      const store = createStore(counter) // 内部第一次调用初始实例
      export default store
    • reducers.js

      用于编写一些修改状态的方法,返回将状态修改后的结果。这个函数用于与创建store对象时进行关联

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      // 包含reducer函数的模块
      import { INCREMENT, DECREMENT } from './actions-types'
      export function counter (state = 0, action) {
      switch (action.type) {
      case INCREMENT:
      return state + action.data
      case DECREMENT:
      return state - action.data
      default:
      return state
      }
      }
    • actions-types.js

      定义actions的type。

      1
      2
      3
      // 包含所有actions type的字符串
      export const INCREMENT = 'INCREMENT'
      export const DECREMENT = 'DECREMENT'
    • actions.js

      生成actions对象的函数

      1
      2
      3
      4
      5
      6
      7
      // 包含所有actions creator
      import { DECREMENT, INCREMENT } from "./actions-types";

      // 增加
      export const increment = (number) => ({ type: INCREMENT, data: number })
      // 减少
      export const decrement = (number) => ({ type: DECREMENT, data: number })
  3. 在入口文件使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    import store from './redux/store'
    function init () {
    ReactDOM.render(
    <App store={store} />,
    document.getElementById('root')
    );
    }
    init()
    store.subscribe(function () {
    init()
    })
  4. 在程序文件中调用

    1
    2
    3
    4
    5
    6
    7
    import { increment, decrement } from '../redux/actions'
    increment = () => {
    // 1. 获取增加的值
    const number = this.select.value * 1
    // 2. 计算新的count并更新
    this.props.store.dispatch(increment(number))
    }

react-redux

官网:https://www.redux.org.cn/

一个用于简化redux操作的react插件。主要对上一步进行改造。

  1. 安装插件

    1
    yarn add react-redux
  2. 修改入口文件

    1
    2
    3
    4
    5
    import { Provider } from 'react-redux';
    ReactDOM.render(
    <Provider store={store}><App /></Provider>,
    document.getElementById('root')
    );
  3. 在app组件中暴露的不是类,而是connect函数

    1
    2
    3
    4
    5
    6
    7
    import { connect } from 'react-redux'
    import { increment, decrement } from '../redux/actions'
    class App extends Component {}
    export default connect((state) => ({ count: state }), {
    increment,
    decrement
    })(App)

异步处理

redux默认不支持异步处理。因此如果在redux中使用异步操作应该使用插件redux-thunk

  1. 安装插件

    1
    yarn add redux-thunk
  2. 修改创建srore对象的结构

    1
    2
    3
    4
    5
    6
    import { createStore, applyMiddleware } from 'redux'
    import { counter } from './reducers'
    import thunk from 'redux-thunk';
    // 生成一个store对象
    const store = createStore(counter, applyMiddleware(thunk)) // 内部第一次调用初始实例
    export default store
  3. 编写异步逻辑,编辑action.js文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 异步action
    export const incrementAsync = (number) => {
    return dispatch => {
    setTimeout(() => {
    // 1s之后才会去执行action
    dispatch(increment(number))
    }, 1000);
    }
    }

redux调试工具

  1. 谷歌浏览器安装插件

    https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd

  2. 项目安装插件

    1
    yarn add redux-devtools-extension
  3. 在项目中引用插件(store中)

    1
    2
    3
    import { composeWithDevTools } from 'redux-devtools-extension'
    import thunk from 'redux-thunk';
    const store = createStore(counter, composeWithDevTools(applyMiddleware(thunk)))

react-redux改造流程

  1. 创建好文件结构——创建redux文件,并创建如下文件

    • actions.js

      包含所有action creator(action的工厂函数)

    • actions-type.js

      包含所有action的type常量

    • reducers.js

      包含N个reducer函数。根据老的strate和action返回一个新的state

    • store.js

      核心管理对象

  2. 入口文件引入

    1
    2
    3
    4
    5
    6
    7
    8
    9
    import React from 'react';
    import ReactDOM from 'react-dom';
    // 引入Provider
    import { Provider } from 'react-redux'
    // 引入核心对象
    import store from './redux/store'
    import App from './components/app/app.jsx'
    // 将根组件进行包裹
    ReactDOM.render((<Provider store={store}><App /></Provider>), document.getElementById('root'));
  3. 在根组件中引用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    import { addComent, deleteComent } from '../../redux/actions'
    class App extends Component {
    static propTypes = {
    comments: propTypes.array.isRequired,
    addComent: propTypes.func.isRequired,
    deleteComent: propTypes.func.isRequired
    }
    }
    // 第一个参数取决于数据定义格式
    export default connect((state) => ({ comments: state }), {
    // 以下来源于action
    addComent,
    deleteComent
    })(App)
  4. 编辑action-type.js文件,定义type的常量

    即对数据操作的类型。

    1
    2
    3
    4
    // 添加评论
    export const ADD_COMMENT = 'add_commont'
    // 删除评论
    export const DELETE_COMMENT = 'delete_comment'
  5. 编辑actions.js文件。

    1
    2
    3
    4
    5
    6
    // 包含所有action creator(action的工厂函数)
    import { ADD_COMMENT, DELETE_COMMENT } from './action-type'
    // 同步添加
    export const addComent = (comment) => ({ type: ADD_COMMENT, data: comment })
    // 同步删除
    export const deleteComent = (index) => ({ type: DELETE_COMMENT, data: index })
  6. 编辑reducer.js文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // 包含N个reducer函数
    // 根据老的strate和action返回一个新的state
    import { ADD_COMMENT, DELETE_COMMENT } from './action-type'

    const initComments = [
    { username: 'Tom', content: 'react挺好的' },
    { username: 'Tom', content: 'react太难了' }
    ]
    export function comments (state = initComments, action) {
    switch (action.type) {
    case ADD_COMMENT:
    return [action.data, ...state]
    case DELETE_COMMENT:
    return state.filter((comment, index) => action.data !== index)
    default:
    return state
    }
    }
  7. 编辑store.js文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 核心管理对象

    import { createStore, applyMiddleware } from 'redux'
    import { comments } from './reducers'
    import thunk from 'redux-thunk'
    import { composeWithDevTools } from 'redux-devtools-extension'
    export default createStore(comments,
    composeWithDevTools(applyMiddleware(thunk))
    )

添加异步操作

  1. actions.js文件中添加异步操作

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // 包含所有action creator(action的工厂函数)
    import { ADD_COMMENT, DELETE_COMMENT, RECEIVE_COMMENTS } from './action-type'
    // 同步接收comments
    const receiveComments = (comments) => ({ type: RECEIVE_COMMENTS, data: comments })
    // 异步获取
    export const getComment = () => {
    return dispatch => {
    // 模拟异步请求
    setTimeout(() => {
    const comments = [
    { username: 'Tom', content: 'react挺好的' },
    { username: 'Tom', content: 'react太难了' }
    ]
    // 分发一个同步actions
    dispatch(receiveComments(comments))
    }, 1000);
    }
    }
  2. actions-type.js中定义类型

    1
    2
    // 异步获取
    export const RECEIVE_COMMENTS = 'receive_comment'
  3. 修改reducers.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // 包含N个reducer函数
    // 根据老的strate和action返回一个新的state
    import { ADD_COMMENT, DELETE_COMMENT, RECEIVE_COMMENTS } from './action-type'
    export function comments (state = initComments, action) {
    switch (action.type) {
    case ADD_COMMENT:
    return [action.data, ...state]
    case DELETE_COMMENT:
    return state.filter((comment, index) => action.data !== index)
    case RECEIVE_COMMENTS:
    return action.data
    default:
    return state
    }
    }
  4. 在根组件的生命周期中触发

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class App extends Component {
    componentDidMount() {
    // 异步获取评论数组
    this.props.getComment()
    }
    }
    // 第一个参数取决于数据定义格式
    export default connect((state) => ({ comments: state }), {
    // 以下来源于action
    addComent,
    deleteComent,
    getComment
    })(App)

reducers暴露多个

1
2
3
4
5
6
7
import { combineReducers } from 'redux'
function counter(){}
function comments(){}
export default combineReducers({
counter,
comments
})

在创建对象时管理的为reducers

1
2
3
4
import reducers from './reducers'
export default createStore(
reducers,
)

此时redux暴露的为对象结构,{counter:xx,comments:xx}