Skip to content

Add HMR and React Fast Refresh support#12060

Open
xhivo97 wants to merge 11 commits into
inventree:masterfrom
xhivo97:enable-hmr-and-react-fast-refresh
Open

Add HMR and React Fast Refresh support#12060
xhivo97 wants to merge 11 commits into
inventree:masterfrom
xhivo97:enable-hmr-and-react-fast-refresh

Conversation

@xhivo97
Copy link
Copy Markdown

@xhivo97 xhivo97 commented Jun 1, 2026

This enables React Fast Refresh in Vite's dev server.

It introduces UseRemotePlugin.tsx, which provides hooks that factor the module loading and rendering logic out of RemoteComponent.

hmrSetModule is only needed to load updated modules for legacy plugins. I'm not sure if they are even developed with Vite. If they are, then this, together with the Vite plugin, should reload the module on file saves, without triggering a full refresh.

The plugin creator PR: inventree/plugin-creator#109

@xhivo97 xhivo97 requested a review from SchrodingersGat as a code owner June 1, 2026 03:41
@netlify
Copy link
Copy Markdown

netlify Bot commented Jun 1, 2026

Deploy Preview for inventree-web-pui-preview ready!

Name Link
🔨 Latest commit 7485ec8
🔍 Latest deploy log https://app.netlify.com/projects/inventree-web-pui-preview/deploys/6a2176a3503c79000853149d
😎 Deploy Preview https://deploy-preview-12060--inventree-web-pui-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 93 (🟢 up 2 from production)
Accessibility: 82 (no change from production)
Best Practices: 100 (no change from production)
SEO: 78 (no change from production)
PWA: -
View the detailed breakdown and full score reports
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@SchrodingersGat SchrodingersGat added the full-run Always do a full QC CI run label Jun 1, 2026
@SchrodingersGat SchrodingersGat added this to the 1.4.0 milestone Jun 1, 2026
@SchrodingersGat SchrodingersGat added plugin Plugin ecosystem User Interface Related to the frontend / User Interface labels Jun 1, 2026
@SchrodingersGat
Copy link
Copy Markdown
Member

This depends on #12058 being merged first - which adds more unit testing for plugin defined UI components

@SchrodingersGat
Copy link
Copy Markdown
Member

@xhivo97 your code needs formatting - run pre-commit install in your local repo, this will automatically handle code formatting on git commit

@xhivo97
Copy link
Copy Markdown
Author

xhivo97 commented Jun 1, 2026

I've added a check for where the module was loaded from, and only load the new module it if it's from the same path. This should fix some of the issues I've found so far.

The original error handling hasn't been added yet, I can hopefully add that today. But that should be the last missing functionality the original had.

Any recommended plugins I should test with this with? I don't know of any using the legacy entrypoint, so having one to test would be great.

@codecov
Copy link
Copy Markdown

codecov Bot commented Jun 1, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.47%. Comparing base (1b8217e) to head (7485ec8).

Additional details and impacted files
@@           Coverage Diff           @@
##           master   #12060   +/-   ##
=======================================
  Coverage   91.47%   91.47%           
=======================================
  Files         979      979           
  Lines       52331    52331           
=======================================
  Hits        47870    47870           
  Misses       4461     4461           
Flag Coverage Δ
backend 90.48% <ø> (ø)
migrations 40.33% <ø> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
Backend Apps 91.77% <ø> (ø)
Backend General 93.53% <ø> (ø)
Frontend ∅ <ø> (∅)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@SchrodingersGat
Copy link
Copy Markdown
Member

SchrodingersGat commented Jun 2, 2026

Below is the testing I have manually performed, with the following terms:

  • New Plugin: A plugin which is built with the new HMR config
  • Old Plugin: A plugin which is built without the HMR config, i.e. before this PR

Tests

Test Condition Result Screenshot
New plugin panel, dev mode Pass image
New plugin, dashboard item, dev mode Pass image
Old plugin, compiled (not dev) Pass image
Old plugin, dev mode Pass image

Observations

  • For the "new" approach (with HMR) the plugin code reloads immediately, it's working very well
  • As far as I can tell, the remote component plugins still all function as expected

Legacy Plugins

Any recommended plugins I should test with this with? I don't know of any using the legacy entrypoint, so having one to test would be great.

I have manually tested with version 0.1.3 of the inventree-test-statistics plugin, and it still loads correctly:

image

And it works with compiled UI code too (note port 8000 rather than 5173):

image

@SchrodingersGat
Copy link
Copy Markdown
Member

Running tests again - the new playwright tests should cover plugin loading sufficiently

@SchrodingersGat
Copy link
Copy Markdown
Member

The original error handling hasn't been added yet, I can hopefully add that today. But that should be the last missing functionality the original had.

@xhivo97 please ping me again when you've added this and I'll review once more

@xhivo97
Copy link
Copy Markdown
Author

xhivo97 commented Jun 2, 2026

@SchrodingersGat Thank you for the review, and especially for testing it. I'm almost done with the error handling but will continue tomorrow since it's a bit late here.

@xhivo97
Copy link
Copy Markdown
Author

xhivo97 commented Jun 2, 2026

Is changing the messages acceptable? I'm thinking maybe it's not allowed because of translations.

So far I have these 4 cases handled, which should be all I think. Maybe these messages could be improved, as well.

Screenshot from 2026-06-02 03-15-30 Screenshot from 2026-06-02 03-13-25 Screenshot from 2026-06-02 03-13-03 Screenshot from 2026-06-02 03-18-53

I also re-added <Boundary> and removed some stuff that I think are not needed anymore since we're not using createRoot anymore. So all that would need reviewing again.

After which, there's some very minor issues like having to manually reload the page if the exported function signature has changed, which are maybe not worth the effort of fixing.

@SchrodingersGat
Copy link
Copy Markdown
Member

Is changing the messages acceptable? I'm thinking maybe it's not allowed because of translations.

It's ok as long as we expose the strings for translation. I am in favor of providing better error feedback here.

After which, there's some very minor issues like having to manually reload the page if the exported function signature has changed, which are maybe not worth the effort of fixing.

Agreed, not worth the effort.

});

return componentFn ? (
<ApiProvider client={queryClient} api={api}>
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SchrodingersGat Is removing these safe?

@xhivo97
Copy link
Copy Markdown
Author

xhivo97 commented Jun 2, 2026

Hope the most recent commit is an improvement. Error handling, and reloadContent is added. I also need to push the HMR plugin to the plugin creator PR.

@SchrodingersGat
Copy link
Copy Markdown
Member

@xhivo97 checked this again, still seems to work across compiled and live plugins, HMR still working nicely too!

A couple of small requests:

Documentation

Please update the documentation around plugin development, especially this section:

image

Changelog

Please add an entry into CHANGELOG.md to indicate that we have updated the plugin loading functionality

@xhivo97 xhivo97 force-pushed the enable-hmr-and-react-fast-refresh branch from 524e253 to 1f385ab Compare June 3, 2026 15:00
@xhivo97
Copy link
Copy Markdown
Author

xhivo97 commented Jun 3, 2026

@SchrodingersGat I just updated the docs and CHANGELOG.md, hope this is OK.

I didn't find any more references to the changes in the docs, but it's possible I have missed some.

@xhivo97
Copy link
Copy Markdown
Author

xhivo97 commented Jun 3, 2026

I am still unsure that this change is OK:

-    <Boundary label={identifierString(`RemoteComponent-${exportName}`)}>
-      <LanguageContext key={remountKey}>
-        {componentFn(pluginContext)}
-      </LanguageContext>
-    </Boundary>
-  ) : errorMsg ? (
-    <Alert
-      color='red'
-      title={t`Error Loading Plugin Content`}
-      icon={<IconExclamationCircle />}
-    >
-      <Text>{errorMsg}</Text>
-    </Alert>
+    <ApiProvider client={queryClient} api={api}>
+      <MantineProvider
+        theme={context.theme}
+        defaultColorScheme={context.colorScheme}
+      >
+        <LanguageContext>{componentFn(context)}</LanguageContext>
+      </MantineProvider>
+    </ApiProvider>

The rationale behind this was to remove the stuff that's supposed to be called only once. But the fact that this seems to break if you remove LanguageContext which also presumably should be called only once per application, similar to MantineProvider, and maybe ApiProvider too?

I don't really understand how all that should work, exactly. So if someone knows, I'd love an explanation.

I also assume this is related to why inventree-test-statistics version 0.1.3 will reset the global theme if you open it, such that you go from dark theme to light theme once you open that panel.

ETA: I looked into this and think this is OK, so in addition I have removed LanguageContext since ThemeContext should already be providing this.

@matmair
Copy link
Copy Markdown
Member

matmair commented Jun 3, 2026

with not creating a new root anymore these removals should be fine

xhivo97 added 3 commits June 4, 2026 03:45
The incoming module needs to include the URL from which it was loaded,
so that it's possible to enforce only loading modules imported from the same
pathname as the current module.
xhivo97 added 4 commits June 4, 2026 03:45
- Add error handling to `useRemotePlugin` and simplify `RemoteComponent`
- Improve HMR to use a registry instead of a single global callback.
  This should now handle two legacy plugin entry points being used at
  the same time via RemoteComponent.
LanguageContext should not be necessary here, as it's provided in
ThemeContext, which is used in InvenTree's frontend entry.
@xhivo97 xhivo97 force-pushed the enable-hmr-and-react-fast-refresh branch from ef62f79 to a5d44f9 Compare June 4, 2026 01:45
@xhivo97
Copy link
Copy Markdown
Author

xhivo97 commented Jun 4, 2026

While not proper fast refresh, if you comment out react() in the Vite config for inventree-test-statistics version 0.1.3, it will use the HMR callbacks to reload the module without reloading the full page. Not sure if that use-case is worth the added HMR related code, but I think I'm happy with that. It does enable non-react plugins to "just work" I suppose, which I think is a useful thing to have.

@SchrodingersGat
Copy link
Copy Markdown
Member

Looks like the broken panel plugin is not providing the expected error message (or any error message):

image

Are you aware of how to download the playwright logs and dig into the errors?

@xhivo97
Copy link
Copy Markdown
Author

xhivo97 commented Jun 4, 2026

@SchrodingersGat I can look into this. Just briefly looked into the logs in the runner and could it be related to this:

[WebServer] 2026-06-04T11:06:13.587977Z [error    ] Static file not found for plugin 'sampleui': plugins/sampleui/sample_panel.js [inventree] ip=127.0.0.1 request_id=df9c10bd-022f-4701-a0f4-ffb8ca85da07 user_id=4

@xhivo97
Copy link
Copy Markdown
Author

xhivo97 commented Jun 4, 2026

@SchrodingersGat It might be that it's looking for this:

$ for i in "origin/enable-hmr-and-react-fast-refresh" "upstream/HEAD"; do printf "$i:\n$(git grep -n 'Error occurred while loading plugin content' $i -- '*.ts' '*.tsx')\n" && echo; done
origin/enable-hmr-and-react-fast-refresh:
origin/enable-hmr-and-react-fast-refresh:src/frontend/tests/pui_plugins.spec.ts:177:  await page.getByText('Error occurred while loading plugin content').waitFor();

upstream/HEAD:
upstream/HEAD:src/frontend/src/components/plugins/RemoteComponent.tsx:147:              {t`Error occurred while loading plugin content`}: {renderingError}
upstream/HEAD:src/frontend/tests/pui_plugins.spec.ts:177:  await page.getByText('Error occurred while loading plugin content').waitFor();

But that has changed and the test probably needs updating

@SchrodingersGat
Copy link
Copy Markdown
Member

https://docs.inventree.org/en/stable/develop/react-frontend/#testing

For how to setup testing locally

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

full-run Always do a full QC CI run plugin Plugin ecosystem User Interface Related to the frontend / User Interface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants