环境准备

  1. 安装插件

    yarn add redux react-redux redux-thunk redux-devtools-extension
  2. 在入口index.js文件中包装

    /**
     * 入口文件
     */
    import React from 'react';
    // 引入redux
    import { Provider } from 'react-redux'
    import ReactDOM from 'react-dom';
    import memoryUtils from './utils/memoryUtils'
    import storageUtils from './utils/storageUtils'
    import App from './App';
    import store from './redux/store'
    const user = storageUtils.getUser()
    memoryUtils.user = user
    ReactDOM.render(
        // 包装
        (<Provider store={store}><App /></Provider>),
        document.getElementById('root')
    );
    
  3. src目录下创建redux文件,分别创建如下文件

    • action-type.js

      /**
       * 包含n个action的type常量标识名称的模块
       */
    • actions.js

      /**
       * 包含n个action creator函数的模块
       * 同步:对象{}
       * 异步:thunk
       */
    • reducer.js

      /**
       * 用来根据老的state和指定的action生成并返回新的state的函数
       */
      import storageUtils from '../utils/storageUtils'
      import { combineReducers } from 'redux'
      
      const initHeadTitle = ''
      // 用来管理头部标题的reducer
      function headTitle (state = initHeadTitle, action) {
        switch (action.type) {
          default:
            return state
        }
      }
      // 用来管理用户的reducer
      const initUser = storageUtils.getUser()
      function User (state = initUser, action) {
        switch (action.type) {
          default:
            return state
        }
      }
      // 向外默认暴露的是合并产生的总的reducer函数
      // 管理的总的结构:
      /**
      {
        headTitle:'首页',
        user:{}
      }
      */
      export default combineReducers({
        headTitle,
        User
      })
    • store.js

      /**
       * redux核心管理对象
       */
      // 向外默认暴露store
      import { createStore, applyMiddleware } from 'redux'
      import thunk from 'redux-thunk'
      import { composeWithDevTools } from 'redux-devtools-extension'
      import reducer from './reducer'
      export default createStore(reducer, composeWithDevTools(applyMiddleware(thunk)))

头部标题

显示

此时已经在redux中存储了值,只需要包装ui组件,使其获取到redux中的值即可。

  1. 编辑header.jsx

    // 1. 引入组件
    import { connect } from 'react-redux'
    
    // 2. 包装暴露对象
    export default connect(
      (state) => ({ headTitle: state.headTitle }),
      {}
    )(withRouter(Header))
    
    // 3. 修改获取title的方法
    const title = this.props.headTitle

修改

修改标题发生在点击左侧菜单栏之后。因此逻辑大概是当点击某个菜单后重新设置redux中的值。

  1. 编辑action-type.js文件,新增一个标识

    export const SET_HEAD_TITLE = 'set_head_title'
  2. 编辑actions.js文件,定义一个设置头部标题的action

    import { SET_HEAD_TITLE } from './action-type'
    //  设置头部标题的同步action
    export const setHeadTitle = (headTitle) => ({ type: SET_HEAD_TITLE, data: headTitle })
  3. 更新reducer.js

    import { SET_HEAD_TITLE } from './action-type'
    // 用来管理头部标题的reducer
    function headTitle (state = initHeadTitle, action) {
        switch (action.type) {
            case SET_HEAD_TITLE:
                return action.data
            default:
                return state
        }
    }
  4. 修改left-nav.jsx文件

    // 1. 引入插件
    import { connect } from 'react-redux'
    // 2. 引入设置标题的actions(函数)
    import { setHeadTitle } from '../../redux/actions'
    // 为点击绑定事件
    if (this.hasAuth(item)) {
        if (!item.children) {
            // 当前要显示的item
            if (item.key === path || path.indexOf(item.key) === 0) {
                // 设置点击事件
                this.props.setHeadTitle(item.title)
            }
            // 向pre中添加
            pre.push(
                <Menu.Item key={item.key}>
                    <IconFont type={item.icon} />
                    <Link
                        to={item.key}
                        // 设置点击事件
                        onClick={() => this.props.setHeadTitle(item.title)}
                        >
                        {item.title}
                    </Link>
                </Menu.Item>
            )
        } 
    }
    // 4. 封装暴露对象
    export default connect((state) => ({}), { setHeadTitle })(withRouter(LeftNav))

用户信息

  1. 定义actions-type

    // 接收用户信息
    export const RECEIVE_USER = 'receive_user'
    // 显示错误信息
    export const SHOW_ERROR_MSG = 'show_error_msg'
  2. 定义actions

    import { SET_HEAD_TITLE, RECEIVE_USER, SHOW_ERROR_MSG } from './action-type'
    import { reqLogin } from '../api'
    import storageUtils from '../utils/storageUtils'
    
    // 接收用户的同步action
    export const recevieUser = (user) => ({ type: RECEIVE_USER, user })
    
    // 显示错误信息的同步action
    export const showErrorMsg = (errorMsg) => ({ type: SHOW_ERROR_MSG, errorMsg })
    
    // 登录的异步action
    export const login = (username, password) => {
      return async dispath => {
        // 1. 执行异步ajax请求
        const result = await reqLogin(username, password)
        if (result.status === 0) {
          // 2.1 成功=>分发 成功的同步action
          const user = result.data
          // 保存到local中
          storageUtils.saveUser(user)
          // 分发接收用户的同步action
          dispath(recevieUser(user))
        } else {
          // 2.2 失败=>分发 失败的同步action
          const msg = result.msg
          // message.error(msg)
          dispath(showErrorMsg(msg))
        }
      }
    }
  3. reducer增加新的处理方式

    import { SET_HEAD_TITLE, RECEIVE_USER, SHOW_ERROR_MSG } from './action-type'
    
    function user (state = initUser, action) {
        switch (action.type) {
            case RECEIVE_USER:
                console.log(action.user);
                return action.user
            case SHOW_ERROR_MSG:
                const errorMsg = action.errorMsg
                return { ...state, errorMsg }
            default:
                return state
        }
    }
  4. login.jsx组件中使用

    // 1. 引入
    import { connect } from 'react-redux'
    import { login } from '../../redux/actions'
    // 2. 包装
    export default connect((state) => ({ user: state.user }), { login })(Login)
    // 3. 使用 (this.props.user)

退出

  1. actions.js

    // 退出登录同步
    export const logout = () => {
        // 删除local中的user
        storageUtils.removeUser()
        return { type: RESET_USER }
    }
  2. reducer.js

    function user (state = initUser, action) {
        switch (action.type) {
            case RECEIVE_USER:
                console.log(action.user);
                return action.user
            case SHOW_ERROR_MSG:
                const errorMsg = action.errorMsg
                return { ...state, errorMsg }
            case RESET_USER:
                return {}
            default:
                return state
        }
    }