环境准备

  1. 安装插件

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    /**
    * 入口文件
    */
    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

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

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

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      /**
      * 用来根据老的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

      1
      2
      3
      4
      5
      6
      7
      8
      9
      /**
      * 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
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 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文件,新增一个标识

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    // 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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    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增加新的处理方式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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
    2
    3
    4
    5
    6
    // 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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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
    }
    }