首页» 前端 » react » 六、使用react-redux、combineReducers、reducer、immutable搭建最佳redux解决方案

六、使用react-redux、combineReducers、reducer、immutable搭建最佳redux解决方案

日期:2019年12月06日 阅读次数:2643 分类:react前端

本文主要分享一个比较完善的redux的解决方案。

  1. 使用react-redux的Provider组件(提供器)包裹所有组件(或直接包裹根组件),使组件层级中的 connect() 方法都能够获得 Redux store。
  2. 使用”combineReducers”函数,把多个reducer(即多个模块)整合成一个大的reducer,并导出给store使用。
  3. 每个模块(如组件)文件夹中有一个自己的store文件夹,只处理本模块的逻辑。
  4. 使用immutable(不可修改的)库,把state转成immutable对象,避免state被修改而出现bug。

目录结构如下:

src
--App.js                  // 根组件
--store                   // 总store目录
  --index.js              // 总store入口
  --reducer.js            // 总reducer

--pages                   // 页面目录
  --header                // 组件
    --index.js            // 组件js文件
    --style.js            // 组件的样式文件

    --store               // 组件下的store(只针对本组件)
      --index.js          // 把本组件store下的constants、actionCreators、reducer一起包装并导出
      --constants.js      // 定义type常量
      --actionCreators.js // 返回action对象
      --reducer.js        // 定义reducer及逻辑

对应文件的内容如下

1、根组件中,使用react-redux的Provider组件(提供器)包裹所有组件

使用react-redux的Provider组件包裹所有组件,把 store 作为 props 传递到每一个被 connect() 包装的组件。

src/App.js

import React from 'react';
import { Provider } from 'react-redux';
import store from './store';
import Header from './pages/header';

function App() {
  return (
    <Provider store={store}>
      <Header/>
      {/*其它更多使用*/}
    </Provider>
  );
}

export default App;

2、子组件中,使用connect链接store

  • 使组件层级中的 connect() 方法都能够获得 Redux store,这样子内部所有组件就都有能力获取store的内容(通过connect链接store)。
  • 可以使用connect的第一个参数mapStateToProps把state映射到props,第二个参数mapDispatchToProps 把dispatch映射到props。
  • 如何设置了设置了第一个参数,state改变后会自动合并到组件的props;如果你省略这个 mapDispatchToProps 参数,默认情况下,dispatch 会注入到你的组件 props 中。

  • 从本组件store中引用actionCreators,在组件中直接供dispatch使用。

src/pages/header/index.js

import React from 'react';
import { HeaderWrapper, SearchWrap, HeadInput } from './style';
import { CSSTransition } from 'react-transition-group';
import { connect } from 'react-redux';
import { actionCreators }  from './store' // 从本组件store中引用actionCreators

const Header = (props) => {
  const { focused, handleFocus, handleBlur } = props;
  return (
    <HeaderWrapper>
      <SearchWrap>
        <CSSTransition
          in={focused}
          timeout={300} // 动画执行1秒
          classNames="slide"
        >
          <HeadInput 
            className = {focused ? "active" : ""}
            onFocus={handleFocus} 
            onBlur={handleBlur}/>
        </CSSTransition>
        <span className = {focused ? "iconfont active" : "iconfont"}></span>
      </SearchWrap>
    </HeaderWrapper>
  )
}

const mapStateToProps = (state) => {
  return {
    // focused: state.get('header').get('focused')
    // 使用immutable的getIn方法连写,效果同上
    focused: state.getIn(['header', 'focused'])
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    handleFocus () {
      dispatch(actionCreators.searchFocus());
    },
    handleBlur () {
      dispatch(actionCreators.searchBlur());
    }
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(Header);

3、总store目录

src/store/reducer.js

import { combineReducers } from 'redux-immutable';
import headerReducer from '../pages/header/store/reducer'; // 引用组件中的子reducer

// 使用combineReducers方法整合多个子reducer
// 使用redux-immutable的combineReducers方法,返回的是immutable对象
const reducer = combineReducers({
  header: headerReducer
})

export default reducer;

src/store/index.js

// 引入createStore方法
import { createStore } from 'redux';
// 引入reducer
import reducer from './reducer';
// 创建store,并把reducer中导出的方法传进来
const store = createStore(reducer);
// 如果要使用Redux DevTools,可如下设置
// const store = createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());

export default store;

4、组件store目录

常量 constants

常量的作用是避免type字符串拼写错误导致出现不易排查的问题。
因为如果type是字符串的话,组件中action的type和reducer中的type如果因拼写错误而不一致,会导致处理失败,但也不会报错,很难排查问题。
而使用了常用,如果拼写错误会报错提示。

src/pages/header/store/constants.js

export const SEARCH_FOCUS = 'header/SEARCH_FOCUS'
export const SEARCH_BLUR = 'header/SEARCH_BLUR'
export const TEST_ADD = 'header/TEST_ADD'

actionCreators

actionCreators是个方法,返回一个action对象,也可以把方法的入参传递给reducer用来更新state

src/pages/header/store/actionCreators.js

import * as constants from './constants'

export const searchFocus = () => ({
  type: constants.SEARCH_FOCUS
})

export const searchBlur = () => ({
  type: constants.SEARCH_BLUR
})

// 可传入数据交给reducer来更新state
export const testAdd = (data) => ({
  type: constants.TEST_ADD,
  data: data
})

reducer 更新state 以及逻辑操作

使用immutable(不可修改的)库,把state转成immutable对象,避免state被修改而出现bug。

src/pages/header/store/reducer.js

import * as constants from './constants' // 导入常量
import { fromJS } from 'immutable'

// 使用immutable库,生成immutable对象(不可变的对象,避免修改state)

const defaultState = fromJS({
  focused: false
})

export default (state = defaultState, action) => {
  const {type} = action;

  switch(type) {
    case constants.SEARCH_FOCUS:
      // immutable对象的set方法,会结合之前的immutable对象的值和设置的值,【返回一个全新的对象】
      return state.set('focused', true);
    case constants.SEARCH_BLUR:
      return state.set('focused', false);
    default:
      return state;
  }
}

组件store的入口文件

整合导入constants、actionCreators、reducer,并一起导出,方便外部统一使用

src/pages/header/store/index.js

import reducer from './reducer'
import * as actionCreators from './actionCreators'
import * as constants from './constants'

export {reducer, actionCreators, constants}

参与文献:
react-redux中文官网API Provider、connect