redux 状态管理进阶使用

Redux 状态管理进阶使用

  1. 实现 state(Reducer) 的分层
  2. Reducer 动态插入
  3. 多个 store 实例实现及调试

1. 实现 state(Reducer) 的分层

前提:redux 中的 Reducer 定义的是一个纯函数,他接收 state | action 两个参数,返回一个新的 state;
combineReducers 是 redux 提供的方法,他可以用来将多个 上述格式 的 Reducer 方法合并成一个函数

1. 一般创建 store 时会通过 combineReducers 将多个 Reducer 合并,生成的 state 结构如下

// 创建 store 时
createStore(combineReducers({
  home: homeReducer,
  admin: adminReducer,
  about: aboutReducer,
  test testReducer,
}));

// 生成的 state 结构
{
  home: {/*...homeState*/},
  admin: {/*...adminState*/}
  about: {/*...aboutState*/}
  test: {/*...testState*/}
}

2. 多层的 Reducer 实现

实际上 combineReducers 参数中的每一项也可以是通过 combineReducers 生成的方法,如下

// 创建 store 时
createStore(combineReducers({
  home: homeReducer,
  test combineReducers({
    testFirst: testFirstReducer,
    testSecond: testSecondReducer
  }),
}));

// 生成的 state 结构
{
  home: {/*...homeState*/},
  test: {
    testFirst: {/*...testFirstState*/},
    testSecond: {/*...testSecondState*/}
  }
}

2. Reducer 动态插入

场景:某个页面的显示是动态,会根据用户权限显示不同的模块,这个时候可以给不同的模块定义不同的 Reducer, 哪个模块显示就将哪个模块的 Reducer 插入到当前运行的 store 中

redux 中的 store 实例具有一个 store.replaceReducer(reducers) 方法,这个方法可以更新当前 store 使用的 reducers, 可以利用这个方法在每次需要插入 reducer 时更新 store,

// @flow

/*
   store的创建函数
*/

import {compose, createStore, applyMiddleware, combineReducers} from 'redux';
import store from '../ReduxStore';

const middleware = [];

// 辅助使用chrome浏览器进行redux调试
const shouldCompose =
  process.env.NODE_ENV !== 'production' &&
  typeof window === 'object' &&
  window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__;

// 创建一个 store 创建函数,这个函数会链接 redux-devtool、绑定中间件、根据 reducer 创建 store 实例
const configureStore = (
  reducer: Function,
  defaultState: ImmutableMap<string, any>,
  newMiddleware?: Array<Function> = [],
  reduxDevToolConfig?: Object,
) => {
  const composeEnhancers = shouldCompose
    ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__(reduxDevToolConfig || {})
    : compose;

  return createStore(
    reducer,
    defaultState,
    composeEnhancers(applyMiddleware(...middleware, ...newMiddleware)),
  );
}

// 功能 将树状结构的 reducer 通过 combineReducer 转化为 redux 可用的结构
function toCombineReducers(reducers: Object) {
  const tempReducers = Object.keys(reducers).reduce((prev, current) => {
    const currentReducer = reducers[current];

    prev[current] = _.isFunction(currentReducer)
      ? currentReducer
      : toCombineReducers(currentReducer);
    return prev;
  }, {});
  return combineReducers(tempReducers);
}

/*
 *  给 store 新增 注册|删除|重置 Reducer 的功能
 * */
type ReducerType = {
  [string]: Function | ReducerType
}

const generateStore = (
  reducer: ReducerType,
  middleware?: Array<Function>,
  reduxDevToolConfig?: Object,
) => {
  // 创建一个 store 实例
  const store = configureStore(
    toCombineReducers(reducer),
    {},
    middleware,
    reduxDevToolConfig,
  );

  /*
  * 给 store 实例挂载两个属性,分别存储创建时的 reducer 和 创建后动态添加的 reducer
  * */
  store.asyncReducers = {};
  store.staticReducers = reducer;

  /*
  * 给 store 实例挂载增加 reducer 的方法
  * */
  store.injectReducer = (
    reducerName: string,
    newReducer: ReducerType,
    parentReducerName?: string, // 这里的 parentReducerName 支持一个字符串:'parent'; 也支持由'.'链接的字符串: 'parent.subParent'
  ) => {
    if (!parentReducerName) {
      const isRepeated =
        _.has(store.staticReducers, reducerName) || _.has(store.asyncReducers, reducerName);

      // reducerName 不可重复
      if (isRepeated) {
        throw new Error(`${reducerName} already exists in store, Please rewrite it`);
      }

      // 更新 reducer
      store.asyncReducers[reducerName] = newReducer;

      // store 更新
      return store.replaceReducer(
        toCombineReducers({
          ...store.staticReducers,
          ...store.asyncReducers,
        }),
      );
    }

    // 由于 asyncReducers 与 staticReducers 不会重复所以以下判断 一正一负或者都是 false
    const isStatic = _.has(store.staticReducers, parentReducerName);
    const isAsync = _.has(store.asyncReducers, parentReducerName);

    if (isStatic) {
      const oldReducer = _.get(store.staticReducers, parentReducerName);

      // 如果是函数则表示没有可扩展设定,需要抛出错误
      if (_.isFunction(oldReducer)) {
        throw new Error(
          `${parentReducerName} is is not extensible, please check ${parentReducerName} module and return a "Object" instead of "Instance" `,
        );
      }

      _.set(store.staticReducers, parentReducerName, {
        ...oldReducer,
        [reducerName]: newReducer,
      });
    } else if (isAsync) {
      const oldReducer = _.get(store.asyncReducers, parentReducerName);

      // 如果是函数则表示没有可扩展设定,需要抛出错误
      if (_.isFunction(oldReducer)) {
        throw new Error(
          `${parentReducerName} is is not extensible, please check ${parentReducerName} module and return a "Object" instead of "Instance" `,
        );
      }

      _.set(store.asyncReducers, parentReducerName, {
        ...oldReducer,
        [reducerName]: newReducer,
      });
    } else {
      // 都为 false 则重新创建一个
      _.set(store.asyncReducers, parentReducerName, {[reducerName]: newReducer});
    }

    return store.replaceReducer(
      toCombineReducers({
        ...store.staticReducers,
        ...store.asyncReducers,
      }),
    );
  };

  // 增加 移除方法
  store.removeReducer = (reducerName: string) => {
    if (_.has(store.asyncReducers, reducerName)) {
      store.asyncReducers = _.omit(store.asyncReducers, [reducerName]);
      store.replaceReducer(
        toCombineReducers({
          ...store.staticReducers,
          ...store.asyncReducers,
        }),
      );
    }
  };

  // 增加重置方法
  store.resetReducer = (excludeAsyncReducer: boolean) => {
    store.replaceReducer(
      toCombineReducers({
        ...store.staticReducers,
        ...(excludeAsyncReducer ? {} : store.asyncReducers),
      }),
    );
  };

  return store;
};

export default generateStore;

// 动态插入 Reducer 
store.injectReducer('storeName', storeNameReducer, 'parent.subParent');

// 最后数据结构
/*
  {
    parent: {
      subParent: {
        storeName: {...storeNameState}
      }
    }
  }
*/

多个 store 实例实现及调试

react-redux 提供的 Provider组件可以用于将 store 与其包裹的子组件进行连接,子组件通过 connect 高阶函数来拿到 store 中的 state; 针对于 多个 store 的情况,connect 会连接到距离最近的父级 Provider

结合上一个例子 ‘动态插入 Reducer ’ 只要使用 generateStore 函数重新创建一个 store 实例即可,使用时需要将新创建的 store 通过 <Provider> 组件绑定到需要的模块上 具体代码参考 - 源码 中的 /src/modules/HomeTwo/ 模块

这里要说明的时 我们在创建 store 时在开发环境会链接到 redux-devtool 在这个链接过程中可以传递一个参数给 当前 store 实例命名,如下

  const composeEnhancers = shouldCompose
    ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({name: 'store 实例名称'}) 
    : compose;

  const store = createStore(
    reducer,
    defaultState,
    composeEnhancers(applyMiddleware(...middleware, ...newMiddleware)),
  );

在开发工具中按如下方法切换实例
在这里插入图片描述

参考

  • 源码 在当前文档的基础上做了深度的封装(flow 做类型检查,请忽略)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
React-Redux是一个React状态管理库,它将Redux集成到React中,使得在React应用中使用Redux更加容易。要使用React-Redux,需要创建一个Redux store并将其提供给React应用。 创建仓库的步骤如下: 1. 安装Redux和React-Redux: ``` npm install redux react-redux ``` 2. 创建store: ``` import { createStore } from 'redux'; import rootReducer from './reducers'; const store = createStore(rootReducer); ``` rootReducer是一个将多个reducer组合在一起的函数。 3. 将store提供给应用: ``` import { Provider } from 'react-redux'; import App from './App'; ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') ); ``` Provider是一个React组件,它将store作为props传递给应用中的所有组件。 使用React-Redux管理状态的步骤如下: 1. 连接组件: ``` import { connect } from 'react-redux'; function mapStateToProps(state) { return { user: state.user }; } function mapDispatchToProps(dispatch) { return { updateUser: (user) => dispatch({ type: 'UPDATE_USER', payload: user }) }; } export default connect(mapStateToProps, mapDispatchToProps)(MyComponent); ``` connect是一个高阶函数,它将组件连接到store。mapStateToProps和mapDispatchToProps是两个函数,它们将store中的状态和dispatch方法映射到组件的props中。 2. 在组件中使用状态: ``` function MyComponent(props) { return ( <div> <h1>{props.user.name}</h1> </div> ); } ``` 在组件中可以通过props来获取状态。 3. 在组件中更新状态: ``` function MyComponent(props) { function handleClick() { props.updateUser({ name: 'New Name' }); } return ( <div> <h1>{props.user.name}</h1> <button onClick={handleClick}>Update Name</button> </div> ); } ``` 在组件中可以通过props来调用dispatch方法来更新状态。这里更新了user的name属性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值