-
-
Notifications
You must be signed in to change notification settings - Fork 70
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: efficient HMR updates #11
Conversation
} | ||
}, [routePath]) | ||
}, [loading]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we put onLoadState
in the deps arrary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think so. We only want the effect to run when loading
changes.
This PR introduces new React hooks for subscribing to hot-reloadable data provided by the "vite-plugin-react-pages/dist/client" module. These hooks include: - useStaticData: Subscribe to the `.staticData` of a specific page or all pages. - usePagePaths: Subscribe to the array of paths for all pages. - usePageModule: Subscribe to the module referred to by the `dataPath` of a specific page. - useTheme: Subscribe to the `Theme` component. In most cases, `useStaticData` is the only hook used by the application/theme. It replaces the `staticData` prop previously passed to the `Theme` component. The idea is to depend on static data where it's needed, instead of having to re-render the entire application when the static data is updated. By calling `useStaticData()`, the component subscribes to **all static data.** By calling `useStaticData(path)`, the component only re-renders when the static data of that specific path is updated. You can even pass a selector if you only need a specific piece of static data. In the example below, the caller only re-renders when the page title is changed. ```ts const title = useStaticData(path, data => data.main.title) ``` The other hooks are mostly for the internal infrastructure of react-pages. `useTheme` is used by the `PageLoader` component (which is now wrapped with `React.memo` to avoid re-rendering when a page is added or removed). `usePagePaths` is used by the `App` component for updating the page routes when a page is added or removed. `usePageModule` is used by the `useAppState` hook (which I've rewritten) for reloading a page when its `dataPath` is changed. It even uses the `/404` page if a path does not exist. Once the path *does* exist, HMR will re-render the `PageLoader` automatically with a new `usePageModule` promise. How does this all work? --- When HMR is enabled, we use a library called [jotai](https://npmjs.org/jotai), which lets us re-render components only when the data they depend on is changed. The `client/state.ts` module (with access to our Jotai atoms) plugs into Vite's HMR API. When the `@!virtual-modules/pages` module is updated, we diff it with its previous module to determine what has changed (see `setPagesAtom` in `client/state.ts`). In production, all that logic is tree-shaked, and we're left with simple hooks that return immutable data.
Thanks! Great feature. I need some time to look into it more carefully. By the way, I have set up basic tests and local playground. You can play with it locally. |
This avoids the issue described in: vitejs/vite#2380
I just pushed 7eb042b, which avoids vitejs/vite#2380, so hot-reloading for |
Sorry for keeping you waiting! I will release it after adding some tests. |
The When you get around to doing that, I can review your work :) |
The goal: Ensure HMR updates for pages never reload the window.
This PR introduces new React hooks for subscribing to hot-reloadable data provided by the
vite-plugin-react-pages/dist/client
module. These hooks include:useStaticData
: Subscribe to the.staticData
of a specific page or all pages.usePagePaths
: Subscribe to the array of paths for all pages.usePageModule
: Subscribe to the module referred to by thedataPath
of a specific page.useTheme
: Subscribe to theTheme
component.In most cases,
useStaticData
is the only hook used by the application/theme. It replaces thestaticData
prop previously passed to theTheme
component. The idea is to depend on static data where it's needed, instead of having to re-render the entire application when the static data is updated.By calling
useStaticData()
, the component subscribes to all static data. By callinguseStaticData(path)
, the component only re-renders when the static data of that specific path is updated. You can even pass a selector if you only need a specific piece of static data. In the example below, the caller only re-renders when the page title is changed.The other hooks are mostly for the internal infrastructure of react-pages.
useTheme
is used by thePageLoader
component (which is now wrapped withReact.memo
to avoid re-rendering when a page is added or removed).usePagePaths
is used by theApp
component for updating the page routes when a page is added or removed.usePageModule
is used by theuseAppState
hook (which I've rewritten) for reloading a page when itsdataPath
is changed. It even uses the/404
page if a path does not exist. Once the path does exist, HMR will re-render thePageLoader
automatically with a newusePageModule
promise.Note: Hot reloading for
useTheme
is broken by a Vite bug. This being tracked by vitejs/vite#2380.How does this all work?
When HMR is enabled, we use a library called jotai, which lets us re-render components only when the data they depend on is changed. The
client/state.ts
module (with access to our Jotai atoms) plugs into Vite's HMR API. When the@!virtual-modules/pages
module is updated, we diff it with its previous module to determine what has changed (seesetPagesAtom
inclient/state.ts
).In production, all that logic is tree-shaked, and we're left with simple hooks that return immutable data.
Depends on #10