Please look for the other parts of the series to fully understand the concept.
Webpack 5 series part-1
Webpack 5 series part-2
Webpack 5 series part-3
Shared state management in Microfrontend
In a micro-frontend architecture, managing shared state between multiple micro-frontends can be challenging, but it can be done in a few ways.
- Using a Shared State Library (Global State)
Redux / Zustand / Recoil: These libraries allow you to manage a global state that can be shared across multiple micro-frontends. You can create a separate micro-frontend responsible for managing the global state (like a store), and expose that state using Webpack's Module Federation.
With Module Federation, you can share libraries like Redux between micro-frontends. You need to ensure that the global store is accessible to all the micro-frontends that need to share state.
new ModuleFederationPlugin({
name: 'app1',
remotes: {
store: 'store@http://localhost:3001/remoteEntry.js'
},
shared: ['react', 'react-dom', 'redux'],
})
- Custom Event System or Event Bus
Use an event-driven architecture where micro-frontends communicate by dispatching and listening to events (custom events). This is a decoupled approach and prevents tight coupling between micro-frontends.
You can implement this using JavaScript's CustomEvent or a more sophisticated event bus.
const event = new CustomEvent('sharedStateChange', { detail: { data } });
window.dispatchEvent(event);
window.addEventListener('sharedStateChange', (event) => {
console.log('Received data:', event.detail.data);
});
- Context API with Module Federation
If you are using React, you can use the Context API to manage shared state across micro-frontends. One micro-frontend can provide a context, and others can consume it.
Example:
Provider Micro-frontend: Provide the context and expose it using Module Federation.
Consumer Micro-frontend: Import the exposed context and consume it.
- Shared Services or APIs
Have a centralized API or service layer that all micro-frontends can communicate with to get or update shared state. This can be a service in a backend or an in-browser shared state service.
For example, you could implement a simple shared service like this:
class SharedStateService {
constructor() {
this.state = {};
this.subscribers = [];
}
getState() {
return this.state;
}
setState(newState) {
this.state = { ...this.state, ...newState };
this.notifySubscribers();
}
subscribe(callback) {
this.subscribers.push(callback);
}
notifySubscribers() {
this.subscribers.forEach(callback => callback(this.state));
}
}
const sharedService = new SharedStateService();
export default sharedService;
All micro-frontends would import this service and subscribe to state changes.
- LocalStorage / SessionStorage
For less complex or smaller-scale use cases, LocalStorage or SessionStorage can be used to persist shared data across micro-frontends. When one micro-frontend updates the state in LocalStorage, the others can read from it.
This approach works well if you need to persist the state temporarily or across page reloads but can become cumbersome with more complex applications.
Tips for Working with Micro-Frontends:
Avoid Global State When Possible: Keep state local to each micro-frontend when possible to reduce coupling.
Use Well-defined Interfaces: Define clear contracts between micro-frontends to avoid unexpected behaviors.
Lazy Loading: Use lazy loading for shared dependencies to improve performance.
Versioning and Compatibility: Ensure that shared dependencies are versioned correctly to avoid conflicts between micro-frontends.
Isolation: Each micro-frontend should be as isolated as possible (style, state, logic) to avoid side effects across different parts of the app.
Top comments (0)