-
Notifications
You must be signed in to change notification settings - Fork 115
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Communication between modules #36
Comments
Communication between modulesReally good question @abdurahmanus, we have come across following patterns for communication among various modules, before I answer the specific details let me list out two specific areas where the modules needs to interact and how we implement in our code. 1) Reading state from other modulesLet us take an example, assume we have three modules
We define the dependency as follows using modules. // CommonModule.js
// -------------------------
function getCommonModule() {
return {
id: "common-module",
reducerMap: {
commonKey: commonReducer
}
}
}
/** ModuleA.js
--------------------------
ModuleA depends on CommonModule, we define the relationship where common module appears in the returned array before moduleA. This ensures that the CommonModule is loaded before ModuleA.
*/
function getModuleA() {
return [
getCommonModule(),
{
id: "module-a",
reducerMap: {
moduleAKey: moduleAReducer
}
}
]
}
/* ModuleB.js
--------------------------
ModuleB also depends on CommonModule, we define the relationship where common module appears in the returned array before moduleB, this ensures that the CommonModule is loaded before ModuleB redux-dynamic-modules does ref counting so it is ok to load the same module multiple times
*/
function getModuleB() {
return [
getCommonModule(),
{
id: "module-b",
reducerMap: {
moduleBKey: moduleBReducer
}
}
]
} Let us now assume we have a component that loads ModuleB, and by that can use state from both CommonModule and ModuleB. We use TypeScript to define interfaces to enforce what part of the state each component can use depending upon what modules they load. By convention ModuleA should not read stateKeys for CommonModule. CommonModule should define set of selectors that act as an API boundary for other modules to read data owned by CommonModule. 2) Initiating changes in state across modulesAs we know any changes to state are in response to Actions. We define shared actions that we call 'SyncActions' these actions can be listened outside module bounderies. The Note: In Redux all reducers get all Actions, so theoretically we don't need Sync actions, but in a large application this could cause a chaos, we use TypeScript for our application and define separate Actions.js file for each module, those Actions.js file is not imported in any other module. This keeps us disciplined on who can react to which actions. Finally coming back to your specific question :). // CommonActions.js
// --------------------------
export const TOGGLE_LAZY_COMPONENT = "CommonActions/TOGGLE_LAZY_COMPONENT";
export function createToggleKayComponent(show: boolean) => { return {
type: TOGGLE_LAZY_COMPONENT,
payload: {
show
}
}} Now the saga/component in LazyComponent can dispatch the action, and the reducer in rootComponent can listen to the action and change its state which eventually will lead to Unmounting/Not rendering the LazyComponent in root component. // RootComponent.js
// --------------------------------------
const RootComponent = ({showLazyComponent:boolean}) => {
if(showLayComponent) {
return <LazyComponent />>
}
return <div>Hello dear</div>
}
const mapStateToProps = (state) => {
return {
showLazyComponent: state.showLazyComponent
}
}
const ConnectedRootComponent = connect(mapStateToProps)(RootComponent); |
@navneet-g Thanks for the detailed answer! Actually I was also going to ask question 1) Reading state from other modules but you responded before. This approach of defining dependencies looks a bit like injecting modules in angular. And two more questions.
|
I have updated the above description to cover selectors.
|
Thanks a lot! Your description really helped me. It would be great to include these examples in the docs or FAQ. Also it would be very helpful to add small typescript project or some samples because it's not obvious how to structure app, where to put types and interfaces. For example, what type will the root state object have? |
This is a really good suggestion and question. To answer these I will write a sample and upload it. Please give me a couple of days. |
@abdurahmanus I have checked in a sample in the above PR, Please look at Typescript Example and let me know if you have questions. |
@navneet-g Thanks! Typescript Example is clear enough. I still have some questions. Some about this example and some general (design) questions. But I don't know is it ok to discuss these questions here in this issue? |
Please feel free to ask here. |
|
Does that make sense? |
What is the preferable way to communicate between modules? Maybe this question is not related directly to this lib. I use redux, redux-saga and redux-dynamic-modules.
The state of my rootModule holds magicBoolean variable. If it becomes true, RootComponent renders SomeLazyComponent, which loads dynamically (I use React.lazy). SomeLazyComponent contains DymanicModuleLoader which loads someDymanicModule. At one point I need to hide SomeLazyComponent and unload someDymanicModule. What I'm trying to do is to fire some action from someDymanicModule (or rootModule) and handle it in reducer (or saga) of rootModule to set magicBoolean variable to false and hide SomeLazyComponent. But in this case I have to import rootActionCreator to someDymanicModule to fire it from there, or to import ROOT_ACTION_NAME to root saga (beacause I need to know action name to handle it). What should I do in this case?
The text was updated successfully, but these errors were encountered: