microsoft / redux-dynamic-modules

Modularize Redux by dynamically loading reducers and middlewares.
https://redux-dynamic-modules.js.org
MIT License
1.07k stars 116 forks source link

How to use connected-react-router with redux-dynamic-module #118

Closed jeevasusej closed 4 years ago

jeevasusej commented 4 years ago

I have created typescript based website with react. I am using react router dom. I want to use connected react router.

I don't know where to add

router: connectRouter(history), and routerMiddleware(history)

I am using the following configuration while create store.

const store: IModuleStore = createStore( { extensions: [getSagaExtension()], } );

I have used the routerMiddleware(history) with the enhancer. But that shows error with the typings.

In my actual structure, I have auth module,

export const AuthModule: ISagaModule = { // Unique id of the module id: 'auth', // Maps the Store key to the reducer reducerMap: { authState: reducer, }, // This module uses redux-saga middleware // This property will be be used by the SagaExtension // to run sagas for the moduleD sagas: [authsaga], };

I am sharing this module with the two container Home and Landing. This will have sub routes.

Where I am making mistakes? How should I correct it?

sinack-old commented 4 years ago

I commented with sample on #114 , so please refer to it. But this demo by JavaScript, I'm sorry if you have TypeScript specific problems.

assainov commented 4 years ago

It might seem intimidating at first, but once you grasp the concept of modules, it becomes very easy.

Whenever you want to integrate any reducers or middleware, you use a module for that. You can create a module called routerModule and add it as an initial module when creating a store. Initial modules can be added as an additional argument to the store.

Here is the basic setup:

routerModule.ts:

import { createBrowserHistory, LocationState } from 'history';
import { connectRouter, routerMiddleware } from 'connected-react-router';
import { IModule } from 'redux-dynamic-modules';

export const history = createBrowserHistory();

export const routerModule: IModule<LocationState> = {
    id: 'startup',
    reducerMap: {
        router: connectRouter(history),
    } as any,
    middlewares: [routerMiddleware(history)],
};

And then use it in your root component as follows:

index.ts:

...
other imports
...
import { ConnectedRouter } from 'connected-react-router';
import { routerModule, history } from './modules/RouterModule';

const store: IModuleStore<{}> = createStore(
    {
        extensions: [getSagaExtension()]
    },
    routerModule,
);

// store.addModule(routerModule); // another way of adding a module

ReactDOM.render(
        <Provider store={store}>
            <ConnectedRouter history={history}>
                <App />
            </ConnectedRouter>
        </Provider>,
        document.getElementById('root'),
    );
jeevasusej commented 4 years ago

Thank you for the response. Do I need to add this routerModule to every module to be used in subsequent modules? For example I am using AuthModule for the initial parent containers which is used in the parent routings (Private/Protected).
Typescript - example

export interface AuthState {
    userstate: UserState;
    locationstate: LocationState;
}
export const _authModule: ISagaModule<types.AuthState> = {
// Unique id of the module
id: 'auth',
// Maps the Store key to the reducer
reducerMap: {
authState: reducer,
},
// This module uses redux-saga middleware
// This property will be be used by the SagaExtension
// to run sagas for the moduleD
sagas: [authsaga],
};

export AuthModule = [routerModule,_authModule];
const mapStateToProps = (state: AuthState) => {
    return {
        isLoggedIn: state.authState.isLoggedIn,
        name: state.authState.name,
        pathname : state.locationstate.router.location.pathname,
    };
};

export const HomeContainer = () => (
    <DynamicModuleLoader modules={[AuthModule]}>
        <ConnectedOrder />
    </DynamicModuleLoader>
);
jeevasusej commented 4 years ago

I commented with sample on #114 , so please refer to it. But this demo by JavaScript, I'm sorry if you have TypeScript specific problems.

Good example. Thank you.

assainov commented 4 years ago

Do I need to add this routerModule to every module to be used in subsequent modules?

@jeevasusej, If you add it as an initial module, there is no need to add it again for subsequent modules.

jeevasusej commented 4 years ago

Thank you @assainov For strongly typed props, I have inherited the interface type from the root state. Will it be a right approach?

export interface AuthState {
    authState: UserState;
}

export interface AuthMainState extends AuthState, RootState {}

const mapStateToProps = (state: AuthMainState ) => {
    return {
        isLoggedIn: state.authState.isLoggedIn,
        name: state.authState.name,
        pathname : state.router.location.pathname,
    };
};
assainov commented 4 years ago

@jeevasusej ,

For strongly typed props, I have inherited the interface type from the root state. Will it be a right approach?

It's getting a bit off topic, but - yes, you can definitely extend it. The alternative approach would be to use a type alias:

type AuthMainState = AuthState | RootState;