为了异步注入reducers,第一步你需要按照你提到的格式编写create store:
减速机
在reducer 中,唯一的区别是获取asyncReducers 作为createReducer 函数的输入,并按以下方式将其用于组合reducer。
function createReducer(asyncReducers) {
return combineReducers({
...asyncReducers,
system,
router,
})
}
配置商店
您的 configureStore 文件应如下所示。我对您的结构进行了一些更改。首先,我在增强器中应用了中间件,以便能够使用 chrome redux DevTool Extention,如果安装了它,否则使用 redux compose,(并且还使用 reducer hot-reloader 来实现异步减速器)。
import { createStore, applyMiddleware, compose } from 'redux'
import { routerMiddleware } from 'react-router-redux'
import thunk from 'redux-thunk'
import createHistory from 'history/createBrowserHistory'
import rootReducer from './reducers'
export const history = createHistory()
const initialState = {}
const middleware = [
thunk,
routerMiddleware(history)
]
const enhancers = [
applyMiddleware(...middlewares),
];
/* eslint-disable no-underscore-dangle */
const composeEnhancers =
process.env.NODE_ENV !== 'production' &&
typeof window === 'object' &&
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
// TODO Try to remove when `react-router-redux` is out of beta, LOCATION_CHANGE should not be fired more than once after hot reloading
// Prevent recomputing reducers for `replaceReducer`
shouldHotReload: false,
})
: compose;
/* eslint-enable */
const store = createStore(
rootReducer(),
initialState,
composeEnhancers(...enhancers)
);
// Extensions
store.injectedReducers = {}; // Reducer registry
/ Make reducers hot reloadable, see http://mxs.is/googmo
/* istanbul ignore next */
if (module.hot) {
module.hot.accept('./reducers', () => {
store.replaceReducer(createReducer(store.injectedReducers));
});
}
export default store;
零件
一个简单的组件将是这样的。正如你在这个组件中看到的,我们首先connect
组件到 react-redux 并且可以使用mapStateToProps
and mapDispatchToProps
,然后为了为这个文件注入reducer,我们需要两件事:
1)reducer文件,2)注入reducer函数
之后,我们将 connect 和 reducerInjected 组合到组件中。
import React from 'react';
import { connect } from 'react-redux';
import { compose } from 'redux';
import reducerForThisComponent from './reducer';
import injectReducer from 'path_to_recuer_injector';
const Component = (props)=><div>Component</div>
function mapStateToProps (state){
return {}
}
const withConnect = connect(mapStateToProps);
const withReducer = injectReducer({ key: 'login', reducerForThisComponent });
export default compose(
withReducer,
withConnect,
)(Component);
注入Reducer.js
该文件可以通过多种方式实现。最佳实践之一是通过 react-boilerplate 实现的。这是用于将减速器注入组件的文件;然而,这个文件还有一个依赖项 ( getInjectors.js
) 可以和 injectReducer.js 一起放在一个 utils 中
import React from 'react';
import PropTypes from 'prop-types';
import hoistNonReactStatics from 'hoist-non-react-statics';
import getInjectors from './getInjectors';
/**
* Dynamically injects a reducer
*
* @param {string} key A key of the reducer
* @param {function} reducer A reducer that will be injected
*
*/
export default ({ key, reducer }) => (WrappedComponent) => {
class ReducerInjector extends React.Component {
static WrappedComponent = WrappedComponent;
static contextTypes = {
store: PropTypes.object.isRequired,
};
static displayName = `withReducer(${(WrappedComponent.displayName || WrappedComponent.name || 'Component')})`;
componentWillMount() {
const { injectReducer } = this.injectors;
injectReducer(key, reducer);
}
injectors = getInjectors(this.context.store);
render() {
return <WrappedComponent {...this.props} />;
}
}
return hoistNonReactStatics(ReducerInjector, WrappedComponent);
};
getInjectors.js
import invariant from 'invariant';
import isEmpty from 'lodash/isEmpty';
import isFunction from 'lodash/isFunction';
import isObject from 'lodash/isObject';
import isString from 'lodash/isString';
import createReducer from '../reducers'; //The createStoreFile
/**
* Validate the shape of redux store
*/
function checkStore(store) {
const shape = {
dispatch: isFunction,
subscribe: isFunction,
getState: isFunction,
replaceReducer: isFunction,
runSaga: isFunction,
injectedReducers: isObject,
injectedSagas: isObject,
};
invariant(
conformsTo(store, shape),
'(app/utils...) injectors: Expected a valid redux store'
);
}
export function injectReducerFactory(store, isValid) {
return function injectReducer(key, reducer) {
if (!isValid) checkStore(store);
invariant(
isString(key) && !isEmpty(key) && isFunction(reducer),
'(app/utils...) injectReducer: Expected `reducer` to be a reducer function'
);
// Check `store.injectedReducers[key] === reducer` for hot reloading when a key is the same but a reducer is different
if (Reflect.has(store.injectedReducers, key) && store.injectedReducers[key] === reducer) return;
store.injectedReducers[key] = reducer; // eslint-disable-line no-param-reassign
store.replaceReducer(createReducer(store.injectedReducers));
};
}
export default function getInjectors(store) {
checkStore(store);
return {
injectReducer: injectReducerFactory(store, true),
};
}
现在一切都设置好了,你拥有了所有的功能,比如reducer注入,甚至在开发阶段支持热modulereducer加载。但是,我强烈建议两件事:
考虑一下这可能是一个好主意,react-boilerplate
因为它提供了许多出色的功能,这些功能是通过专注于大型应用程序的最佳实践实现的。
如果您计划进行代码拆分,则意味着您将拥有一个具有可扩展性问题的应用程序。因此,我建议不要使用 redux-thunk 而是使用 redux saga。最好的解决方案是Inject saga middlewares asynchronously
在组件卸载后立即弹出 saga 文件。这种做法可以通过多种方式改进您的应用程序。