Redux理解

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

image-20201115141633819

需要Redux时的情景

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

Redux基本使用

  1. 安装

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

    • store.js

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

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

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

      // 包含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。

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

      生成actions对象的函数

      // 包含所有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. 在入口文件使用

    import store from './redux/store'
    function init () {
        ReactDOM.render(
            <App store={store} />,
    document.getElementById('root')
    );
    }
    init()
    store.subscribe(function () {
        init()
    })
  4. 在程序文件中调用

    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. 安装插件

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

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

    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. 安装插件

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

    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文件

    // 异步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. 项目安装插件

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

    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. 入口文件引入

    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. 在根组件中引用

    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的常量

    即对数据操作的类型。

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

    // 包含所有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文件

    // 包含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文件

    // 核心管理对象
    
    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文件中添加异步操作

    // 包含所有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中定义类型

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

    // 包含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. 在根组件的生命周期中触发

    class App extends Component {
        componentDidMount() {
            // 异步获取评论数组
            this.props.getComment()
        }
    }
    // 第一个参数取决于数据定义格式
    export default connect((state) => ({ comments: state }), {
        // 以下来源于action
        addComent,
        deleteComent,
        getComment
    })(App)

reducers暴露多个

import { combineReducers } from 'redux'
function counter(){}
function comments(){}
export default combineReducers({
    counter,
    comments
})

在创建对象时管理的为reducers

import reducers from './reducers'
export default createStore(
    reducers,
)

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