A simplified prospective in sharing Redux Store between Federated React Apps
Tags: typescript, react
Published: 2023-09-13 21:27:43



You have already created multiple federated react apps, and you still wondering, how can I share not just components in the runtime, but the entire store between the apps.

There are two notes here to consider:

1. Module Federation is meant to share any JS/TS module not just UI component.
2. The key into sharing the store is by exposing the `reducers` from our app into the host.


## Work in action!!

Firstly clone the repo, or [click here](https://github.dev/IbrahimShamma99/gentle-intro-module-federation-react) to navigate through the code without installing

```sh
git clone https://github.com/IbrahimShamma99/gentle-intro-module-federation-react 
cd ./gentle-intro-module-federation-react
```

Now let's examine the Repo, we have two react `apps` built with webpack, `app1` and `host`

- `app1`: will contain the store that will be exposed

- `host`: it will consume the store from `app1`


So now how can we share the store?

### Remote set up

While we are preparing the store in order to be consumed, we are here preparing a normal store that will consumed normally inside the remote (remote and `app1` are the same I will use both interchangeably), and we will expose the reduces and that's it!

#### Exposing the Store in action! 

first let's set up a store and reducer inside the remote
Firstly we are setting up a layout slice that will handle storing and toggling theme.

```ts 
// apps/app1/src/reducer.ts

import { createSlice } from "@reduxjs/toolkit";

type Theme = "light" | "dark";

export interface LayoutState {
  theme: Theme;
}

const initialState: LayoutState = {
  theme: "light",
};

const layoutSlice = createSlice({
  name: "layout",
  initialState,
  reducers: {
    toggleTheme: (state, action) => {
      state.theme = action.payload as Theme;
    },
  },
});

export const { toggleTheme } = layoutSlice.actions;

export { layoutSlice };

export default layoutSlice;
```

And Now besides the reducer, we will create the store, although we will not share the store between apps, since redux only combines reducers not stores, but the store is crucial for the mutating the reducer inside the remote

> NOTE: You can expose the reducers and be able to access the store, but preferably don't use actions outside of the remote

To create the remote store by simply:
```ts
import { configureStore } from "@reduxjs/toolkit";
import { useDispatch } from "react-redux";
import { combineReducers } from "redux";
import { layoutSlice } from "./reducer";

const Store = configureStore({
  reducer: combineReducers({
    layout: layoutSlice.reducer,
  }),
});

export { Store };
export type RootState = ReturnType<typeof Store.getState>;
export type AppDispatch = typeof Store.dispatch;
export const useAppDispatch = () => useDispatch<AppDispatch>();
```

now in module federation remote app configuration we need to do the following 

```js
// apps/app1/configs/federationConfig.js
const dependencies = require("../package.json").dependencies;

module.exports = {
  name: "app1",
  filename: "remoteEntry.js",
//This is what is important, exposing our reducer
  exposes: {
    "./layout-slice": "./src/reducer",
  },
  shared: {
    ...dependencies,
    react: {
      singleton: true,
      requiredVersion: dependencies["react"],
    },
    "react-dom": {
      singleton: true,
      requiredVersion: dependencies["react-dom"],
    },
  },
};
```

Now we have finished setting up and exposing the remote store, now we go into the host part

### Host set up

Now we need to do one unconventional thing in the redux world, instead of importing all of our reducers like what we do normally synchronously, we will dynamically build the store, and we will have two approaches

- When loading the page wait until the Store is imported then continue upon rendering.
- Load Redux microfrontend framework [Click Here](https://github.com/microsoft/redux-micro-frontend/wiki)


we will use the first approach since the latter needs a blog by its own.

we need to set up the host store now:

```ts
//apps/host/src/store.tsx

import { configureStore } from "@reduxjs/toolkit";
import { combineReducers } from "redux";

/*
NOTE: 
Here you take the interface of each slice and pass them into the `useStoreSelector` Hook in this way your `intellisense` will be aware of the federated types.
*/
const federatedSlices = {
  layout: await import("app1/layout-slice").then(
    (module) => module.default.reducer
  ),
};

const initStore = async () => {
  const Store = configureStore({
    reducer: combineReducers({
      ...federatedSlices,
    }),
  });
  return Store;
};

export default initStore;
```

As you can see in the code the store is initialized asynchronously, so the dom will be waiting our `initStore` promise to be fulfilled:

```tsx
//apps/host/src/bootstrap.tsx

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import { Provider } from "react-redux";
import initStore from "./store";

const root = ReactDOM.createRoot(
  document.getElementById("root") as HTMLElement
);

initStore().then((Store) => {
  root.render(
    <Provider store={Store}>
      <App />
    </Provider>
  );
});
```

> NOTE make sure that the host already connected into the remote via config, if you could not make a lot of sense here, better to look up the following [article](https://dev.to/ibrahimshamma99/gentle-introduction-into-module-federation-the-react-parts-4ehc).


#### Cheers!