Git Product home page Git Product logo

Comments (17)

joeldenning avatar joeldenning commented on May 13, 2024 23

Happy to hear you're considering single-spa or at least looking at building a system that operates similarly. Redux-like state management is not built into single-spa itself, but you can definitely use redux or any other state management system in addition to single-spa. The way you would do this is to have redux in a common dependencies bundle and then have all the child applications import the redux store and use it inside their application.

How to share state between single-spa child applications is a common question, and one that I think there still is room for innovation and improvement. At Canopy, we use single-spa in production with 14 child applications, and we have a few techniques for sharing state. We also adopted a convention that we like that simplifies state management.

A simplifying convention

At Canopy, we have only one "main content" child application that is active at any time. There are still multiple applications active at once, but only one of them is rendering the main content that is scrollable. The rest of them (so far) just render fixed position menus. As you're navigating around, the menus might stay in place when the main content applications are switching off. So in the screenshot below, communications-ui is the main content application that is scrollable, and primary-navbar, contact-menu, and workflow-ui are supplementary applications that just are fixed menus.

screen shot 2017-08-18 at 12 09 08 pm

Canopy techniques for sharing state

We chose not to have a global redux store or state manager at Canopy. What we have preferred is to have each of the child applications manage their own state and be self encapsulated. We were worried that a global store would lead to weird situations where the order in which you navigate around to applications matters. In other words, we wanted to avoid where navigating to App1 and then to App2 would result in a different global state (and bugs) than navigating to App2 and then to App1. Another reason we have liked doing things that way is that we deploy the child applications separately and it is nice to have a blast radius of each deployment. We were worried that having a global state manager would create situations where you couldn't really deploy one of the child applications without worrying about how it interacted with the other apps and the global state.

As an alternative, we use the following state sharing techniques:

  • Having a shared API layer that can cache api objects. What we realized is that a lot of the state that needs to be shared between child applications is just API data. The UI-state that isn't in the API is much less commonly shared. So we built an ajax library that allows child applications to fetch data from the server like normal, but sometimes the data is actually cached and an ajax request isn't really made. This way, the child applications don't even need to know if any other child application is fetching the same data, they just make the API request like normal and that ajax library takes care of not making too many ajax requests to the same endpoint. This solves a surprisingly high percentage of our data sharing needs.
  • Apps that export observables to other apps. For this one, we choose an owning application for a piece of UI state and then we have that application export an observable for that data. All other applications subscribe to the observable, but can't change the values in it. We don't use this very often, but have found a few use cases where we like it. It's not quite a global store since there is an owning application for the state, and we have also found it pretty simple to debug/interact with because only the owning application updates the data.
  • App1 imports a function from App2 and calls it. What we like about this is that it is explicit and easy to follow where the data is originating and where it's going.

Anyway, that is how we do it at Canopy. But like I said above, I think this is an area where there could be more innovation. If you guys find a better way to do things, we'd be happy to hear what you've done!

from single-spa.

joeldenning avatar joeldenning commented on May 13, 2024 8

Shared API layer - where does it store the cached data? and how is it accessible from all child-apps?

The shared api layer is accessible to all child apps via an import statement. We have a javascript module called "fetcher" which any child application can import. In our case, we are using SystemJS as our module registry, but this would also work with webpack, assuming there is one master webpack config for everything. So example code would look like this:

// In our case, fetcher is a module registered to SystemJS. But module resolution could also be done by webpack if you're using that.
import {getWithSharedCache} from 'fetcher';

// Get this api object, but share it with all other child applications. Once the user navigates away from user 1's settings page, clear the cache.
// Our specific implementation returns an observable, but it could just as easily return a Promise.
getWithSharedCache('/api/users/1', location => location.href.startsWith('/users/1/settings'))
.subscribe(...)

Apps that export observables to other apps - is it being exported due the global window object? otherwise how child-apps has access to this observables?

We do not put the observables on the window, but instead make them exported values from the javascript module for the child application. That way, other child applications can access the observable just with an import.

// child app 1, which exposes an observable
import {Observable} from 'rx'; // The rx library is an implementation of observables

// Single-spa lifecycles, as per normal for a child application
export function bootstrap(props) {...}
export function mount(props) {...}
export function unmount(props) {...}

// Observable for others to import
export const heightOfNavbar = Observable.just(4);
// child app 2, which needs the height of the navbar
import {heightOfNavbar} from '../path-to-child-app-1/child-app-1.js';

heightOfNavbar.subscribe(
  height => console.log('height', height)
);

Did you consider post messages protocol for establishing communication between child-apps?

My understanding of postMessage is that it allows for communication between two different window objects (which are usually iframes or browser tabs opened up from a parent tab). Assuming that my understanding of postMessage is correct, I don't think that it will work for communicating between single-spa child applications because all of those child applications are sharing the same window object (since they are all in the same browser tab and there are not usually inside of iframes).

It would be great if you add to the examples repository an example of this technics.

I agree! I just created #112 for this. Hopefully we'll find a good way to help others understand their options and how to implement inter-app communication. Contributions accepted, of course.

from single-spa.

ShayHaluba avatar ShayHaluba commented on May 13, 2024 2

Hi @joeldenning,

I have some questions about your state sharing techniques:

  • Shared API layer - where does it store the cached data? and how is it accessible from all child-apps?

  • Apps that export observables to other apps - is it being exported due the global window object? otherwise how child-apps has access to this observables?

  • Did you consider post messages protocol for establishing communication between child-apps?

It would be great if you add to the examples repository an example of this technics.

Thanks 👍

from single-spa.

blittle avatar blittle commented on May 13, 2024 1

Another option would be to use a simple pub/sub event emitter. We have preferred observables because you can control if your subscription includes events that happened in the past before your subscription started (useful when two apps are mounting and you can't guarantee the load order).

from single-spa.

 avatar commented on May 13, 2024 1

You might want to use something like postal.js
https://github.com/postaljs/postal.js

we have been using this for sometime now in production without issues

from single-spa.

joeldenning avatar joeldenning commented on May 13, 2024

Closing -- feel free to reopen if there are more questions.

from single-spa.

abelkbil avatar abelkbil commented on May 13, 2024

@me-12 @joeldenning Hope https://github.com/arkency/event-bus can do the inter spa communications. I have a doubt on socket connection from app one will be closed if the app is unmounted ? , Hope that is okay ?

from single-spa.

me-12 avatar me-12 commented on May 13, 2024

@abelkbil Have a look at my example app which uses redux for communication between apps: https://github.com/me-12/single-spa-portal-example Maybe that's helpful to you.

from single-spa.

joeldenning avatar joeldenning commented on May 13, 2024

@abelkbil It looks like eventing-bus allows you to unregister a single subscription or even unregister all subscriptions. If you unregister your subscriptions during the unmount lifecycle, your unmounted applications should no longer receive messages on the event bus once they are unmounted.

Let me know if I'm misunderstanding or if you have more questions! @me-12's example repo is a good place to look, too!

from single-spa.

abelkbil avatar abelkbil commented on May 13, 2024

@joeldenning Thanks I understand eventing-bus subscription model.

from single-spa.

nchandran030 avatar nchandran030 commented on May 13, 2024

Hi Joel this is nitheesh I am using single spa application concept for my project here i am having 6 independent application all the applications are in Angular now I want to pass the data between two independent application can u please help me here.

https://github.com/nchandran030/single-spa-main-application

from single-spa.

joeldenning avatar joeldenning commented on May 13, 2024

Hi @nchandran030 - could you clarify what you need help with?

from single-spa.

joeldenning avatar joeldenning commented on May 13, 2024

Ah I see now that your comment is a duplicate of #552 (comment). I will respond there, instead.

from single-spa.

SENTHILnew avatar SENTHILnew commented on May 13, 2024

@nchandran030 Take a look at my repo https://github.com/SENTHILnew/micro_spa_intercom
similiar way you can create a redux store and pass its ref to microapps through custom props

from single-spa.

smrutiwankhade avatar smrutiwankhade commented on May 13, 2024

Hi Team,

Actually I am facing issue while accessing the another application from container app. It gives me CORS issue while loading the application itself.

from single-spa.

joeldenning avatar joeldenning commented on May 13, 2024

CORS issues are resolved in the normal way - adding an Access-Control-Allow-Origin header to the server hosting the files. See https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS/Errors

from single-spa.

shrinidhiurala avatar shrinidhiurala commented on May 13, 2024

You might want to use something like postal.js
https://github.com/postaljs/postal.js

we have been using this for sometime now in production without issues

it'll be great if you can share an example code for how to use it with single-spa? Thanks

from single-spa.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.