Sp-dev-docs: Sharing code/dependencies between Web Part and Application Customizer

Created on 14 Jan 2019  ·  15Comments  ·  Source: SharePoint/sp-dev-docs

FYI, TLDR: I added comment which summarizes the main problem here https://github.com/SharePoint/sp-dev-docs/issues/3259#issuecomment-454753467.

Category

  • [x] Question
  • [ ] Typo
  • [ ] Bug
  • [ ] Additional article idea

I will start with the question first and provide some details later.

Question

How can I use third party libraries (as well as our own, private node package [DLL]) and make sure they are only loaded/initialized once, even when there are multiple component types (web parts, application customizer) on a page?

I would expect external dependencies to be loaded (and initialized) only once, even if they are used in several components on one page. This doesn't seem to be the case, for example we get warnings like there are multiple mobx instances active.

Situation

We are building an intranet solution that contains web parts (extend BaseClientSideWebPart) as well as some components in header and footer (extend BaseApplicationCustomizer).

We have common code coming from our own, private npm module that is referenced in the package.json of our SharePoint app. I will refer to this as "our library". Our library uses external dependencies like mobx, styled-components and several others. Some of these dependencies are bundled directly in to our library, others are loaded by SPFx (specified as externals in config.json).

Basically everything works. I.e. it works if we are

  • on a page where there is -only- a web part (no header/footer)
  • on a page where there is -only- the application customizer, but no web part.

But we have problems when there are both component types on the same page. Some of the problems we get are

  • [mobx] Warning: there are multiple mobx instances active. This might lead to unexpected results. See https://github.com/mobxjs/mobx/issues/1082 for details.
  • Styled components: The theme is null - this could be related to multiple instances of Styled Components running on the same page.

Long story short: I need a solution for this. I found and tried several different things:

  • Add a vendor chunk in our library and load that via externals in config.json and by specifying it as a globalDependencies of our library. After this, the application still worked as before, i.e. nothing broke, but the initial problems were still here.
  • I also found this post: https://blog.mastykarz.nl/building-shared-code-sharepoint-framework-revisited/ - but didn't even try that, as apparently it's not supported in production yet.
Author Feedback spfx-general no-recent-activity fixed question

All 15 comments

Thank you for reporting this issue. We will be triaging your incoming issue as soon as possible.

@VesaJuvonen, @pgonzal : I'm replying to your comments from #467 as that issue is closed.

We have exactly that situation: We want to share code (from a bundling/network/loading/performance perspective) as well as from a runtime perspective (only one instance of singletons, external dependencies, etc.):

  • Regarding the bundling: Not much needs to be said in regards to this, as with such an approach code physically is only loaded once.
  • Regarding the runtime: I guess this is a common scenario where different components on the page need to share common state in order to communicate. Be this between different web parts or between application customizers and footers and web parts, etc.

To be honest I am surprised that there aren't more customers that have requirements like this.

Update: I found a couple of resources that create one bundle for different web parts (the default behavior is that for every component an own bundle is created), e.g. this one: https://www.eliostruyf.com/creating-multi-component-bundles-in-sharepoint-framework-solutions/

I now did the same, with following result:

  • Application Customizer and Web Part are both in the same physical JS file (i.e. in the same bundle in config.json)
  • On a page where both are placed, the JS file is obviously loaded via the network only once, which is good
  • The initial problems still persist, i.e. there are multiple mobx instances active, etc.
  • If I place the same web part one the same page multiple times (and no application customizer), then I don't get the errors.

This means, as far as I understand, that SPFx somehow executes the whole script again, leading to multiple instances/contexts being created. But only when there are application customizers _and_ web parts on the page. Is this correct? Is there anyway to work around this? Can I share code between an application customizer and a web part?

Ok, after some further analysis I would like to summarize my findings:

When a web part (BaseClientSideWebPart) and a application customizer (BaseApplicationCustomizer) are placed on the same page, they are both instantiated independently. This means they run in completely unrelated contexts. So for example

  • export const foo = Math.rand() will be executed once for the web part and once for the application customizer. From a naive developer perspective I would expect this to be executed only once per solution.
  • Also static fields on a class might behave wrong.
  • Dependencies (as in my case mobx, styled-components and most probably others) might not work correctly, as they assume there is only one instance of themselves in the page.
  • And most probably there are also other issues...

As far as I understand, the SPFx team is aware of this (https://github.com/SharePoint/sp-dev-docs/issues/467#issuecomment-321548410 and others) since one and a half years, but are not sure if this is an actual problem.

I see this as a major problem, as it prevents developers from reliably sharing code between the different components on the page. Sure, one could argue that libraries like styled-components should be able to deal with multiple instance on a page (especially if it is the same version), on the other hand I believe that SPFx should only initialize the code once, due to the other, above mentioned problems.

Some related resources are:

@patmill, @VesaJuvonen Is there any work around for this? Or are the any plans to change this in the near future?

There is a _solution_ for this currently under dev / in the roadmap that you can monitor & vote up in uservoice. In summary, it adds a new component of type library that multiple components can depend on.

However, you can implement this today... nothing specific to SharePoint IMHO... it's a pattern that's common in normal web dev. Create a separate library and publish it as an NPM package. You can then get that library hosted in a CDN (one you maintain, or public one if you have enough 👍 / ⭐. Then just make sure you included it as an external reference. This will tell the hosted SPFx implementation of system.js to only load the module once on the page.

So... there's a solution to this challenge today, but MSFT is also working on a solution for SPFx-specific scenarios.

Thanks for your reply @andrewconnell. I already up voted in the user voice and am aware of the library type. And I am also aware of the "npm package" approach, as this is exactly what we are doing:

  • We have an own (private) npm package, that we host on our Azure CDN
  • This package is referenced as an external in config.json
  • However, even though the physical JS file is only loaded once over the wire, it is still instantiated twice by SPFx (once for the Application Customizer and once for the Web Part)
  • The interseting thing is that it is only instantiated once per component type, that means:

    • if I have two Web Parts on the same page, it is only instantiated once

    • if I have the Application Customizer and a Web Part on the page, it is instantiated twice

Above behavior made me create a very simple dummy project to show this without having any dependencies: https://github.com/PzYon/spfx-code-share

Basically what i do there is I have following line of code:
export const SimpleConstValue = Math.random();

This is rendered by a react component that is used by a web part and the application customizer. When I add the web part twice to the page, this is the outcome:

image

As you can see, the value (i.e. Math.random()) is executed twice, once for the Application Customizer and once for the Web Part. And for the two web part instances, it's the same value.

And I believe that's the problem. So it isn't related to externals, etc. but to the way that SPFx (or system.js) loads and initializes the different component types available in SPFx. It appears that the context or scope or however you want to call it is shared, but only per component type.

Can you confirm this? Or am I doing something wrong?

We have the same problem.
And it's annoying because some libraries just don't work :/

Problem (and solution) is understood. Not ignoring the thread, we just can't mark it as fixed at the moment.

Ok. So that means that's just the way it is and there is no workaround at this moment. Correct? Do you have any idea, when this will be fixed, as I understand you already know the solution.

Any updates on this? It's causing us a lot of problems in the meanwhile...

The scenario should now be possible through the Library feature we introduced in the latest release of SPFx.
Can you please confirm it and, if yes, close this issue?

This issue has been automatically marked as stale because it has marked as requiring author feedback but has not had any activity for 7 days. It will be closed if no further activity occurs within next 7 days of this comment. Thank you for your contributions to SharePoint Developer activities.

Closing issue due no response from original author. If this issue is still occurring, please open a new issue with additional details. Notice that if you have included another related issue as additional comment on this, please open that also as separate issue, so that we can track it independently.

Issues that have been closed & had no follow-up activity for at least 7 days are automatically locked. Please refer to our wiki for more details, including how to remediate this action if you feel this was done prematurely or in error: Issue List: Our approach to locked issues

Was this page helpful?
0 / 5 - 0 ratings

Related issues

SteIvanov picture SteIvanov  ·  3Comments

karishmaTCS picture karishmaTCS  ·  3Comments

jonthenerd picture jonthenerd  ·  3Comments

StfBauer picture StfBauer  ·  3Comments

jonthenerd picture jonthenerd  ·  3Comments