diff --git a/.changeset/tricky-moons-dress.md b/.changeset/tricky-moons-dress.md new file mode 100644 index 000000000..cf5855d6c --- /dev/null +++ b/.changeset/tricky-moons-dress.md @@ -0,0 +1,5 @@ +--- +"@squide/firefly": patch +--- + +Fix useProtectedDataQueries isReady result value. diff --git a/docs/guides/add-authentication.md b/docs/guides/add-authentication.md index b488b9841..3738ba830 100644 --- a/docs/guides/add-authentication.md +++ b/docs/guides/add-authentication.md @@ -631,7 +631,7 @@ export function RootLayout() { Finally, assemble everything: -```tsx !#16,20,30-33 host/src/register.tsx +```tsx !#13,16,20,22,30-33 host/src/register.tsx import { PublicRoutes, ProtectedRoutes, type ModuleRegisterFunction, type FireflyRuntime } from "@squide/firefly"; import { RootLayout } from "./Rootlayout.tsx"; import { AuthenticationBoundary } from "./AuthenticationBoundary.tsx"; diff --git a/docs/guides/develop-a-module-in-isolation.md b/docs/guides/develop-a-module-in-isolation.md index a64953e05..c083249be 100644 --- a/docs/guides/develop-a-module-in-isolation.md +++ b/docs/guides/develop-a-module-in-isolation.md @@ -27,7 +27,7 @@ host ## Create a shell package !!!info -The implementation details of the `RootLayout` and `RootErrorBoundary` components won't be covered by this guide as it already has been covered many times by other guides. +The implementation details of the `RootLayout`, `RootErrorBoundary` and `ModuleErrorBoundary` components won't be covered by this guide as it already has been covered many times by other guides. !!! First, create a new package (we'll refer to ours as `shell`) and add the following fields to the `package.json` file: @@ -54,6 +54,7 @@ Then, create an `AppRouter` component in the shell package to provide a **reusab ```tsx shell/src/AppRouter.tsx import { AppRouter as FireflyAppRouter } from "@squide/firefly"; import { RouterProvider, createBrowserRouter } from "react-router-dom"; +import { RootErrorBoundary } from "./RootErrorBoundary.tsx"; export function FireflyAppRouter() { return ( @@ -64,6 +65,7 @@ export function FireflyAppRouter() { router={createBrowserRouter([ { element: rootRoute, + errorElement: children: registeredRoutes } ])} @@ -81,14 +83,14 @@ Finally, create a local module to register the **application shell**. This modul ```tsx shell/src/register.tsx import { PublicRoutes, ProtectedRoutes, type ModuleRegisterFunction, type FireflyRuntime } from "@squide/firefly"; import { RootLayout } from "./RootLayout.tsx"; -import { RootErrorBoundary } from "./RootErrorBoundary.tsx"; +import { ModuleErrorBoundary } from "./ModuleErrorBoundary.tsx"; export const registerShell: ModuleRegisterFunction = runtime => { runtime.registerRoute({ element: , children: [ { - errorElement: , + errorElement: , children: [ PublicRoutes, ProtectedRoutes @@ -102,7 +104,7 @@ export const registerShell: ModuleRegisterFunction = runtime => ``` !!!info -This guide only covers the `RootLayout` and `RootErrorBoundary` components but the same goes for other shell assets such as an `AuthenticationBoundary` component. +This guide only covers the `RootLayout`, `RootErrorBoundary` and `ModuleErrorBoundary` components but the same goes for other shell assets such as an `AuthenticationBoundary` component. !!! ## Update the host application diff --git a/docs/guides/isolate-module-failures.md b/docs/guides/isolate-module-failures.md index cb89e3f99..e8a97b0a1 100644 --- a/docs/guides/isolate-module-failures.md +++ b/docs/guides/isolate-module-failures.md @@ -12,10 +12,10 @@ Nevertheless, an application, federated or non-federated, can get very close to ## Create an error boundary -First, define a React Router's error boundary to catch module errors. For this example we'll name it `RootErrorBoundary`: +First, define a React Router's error boundary to catch module errors. For this example we'll name it `ModuleErrorBoundary`: -```tsx host/src/RootErrorBoundary.tsx -export function RootErrorBoundary() { +```tsx host/src/ModuleErrorBoundary.tsx +export function ModuleErrorBoundary() { return (
An error occured while rendering a page from a module!
) @@ -24,22 +24,22 @@ export function RootErrorBoundary() { ## Register the error boundary -Then, update the host application `registerHost` function to declare the `RootErrorBoundary` component below the `RootLayout` component but above the routes of the modules. By doing so, if a module encounters an unhandled error, the error boundary will only replace the section rendered by the `Outlet` component within the root layout rather than the entire page. +Then, update the host application `registerHost` function to declare the `ModuleErrorBoundary` component below the `RootLayout` component but above the routes of the modules. By doing so, if a module encounters an unhandled error, the error boundary will only replace the section rendered by the `Outlet` component within the root layout rather than the entire page. A React Router's error boundary is declared with the [errorElement](https://reactrouter.com/en/main/route/error-element) of a route: ```tsx !#7,11 host/src/register.tsx import { PublicRoutes, ProtectedRoutes, type ModuleRegisterFunction, type FireflyRuntime } from "@squide/firefly"; import { RootLayout } from "./RootLayout.tsx"; -import { RootErrorBoundary } from "./RootErrorBoundary.tsx"; +import { ModuleErrorBoundary } from "./ModuleErrorBoundary.tsx"; export const registerHost: ModuleRegisterFunction = runtime => { runtime.registerRoute({ element: , children: [ { - // Default error boundary. - errorElement: , + // Error boundary for modules. + errorElement: , children: [ PublicRoutes, ProtectedRoutes @@ -56,7 +56,7 @@ By implementing this mechanism, the level of failure isolation achieved is **com ### Hoisted pages -If your application is [hoisting pages](../reference/runtime/runtime-class.md#register-an-hoisted-route), it's important to note that they will be rendered outside of the host application's `RootErrorBoundary` component. To prevent breaking the entire application when an hoisted page encounters unhandled errors, it is highly recommended to declare a React Router's error boundary for each hoisted page as well, again using [errorElement](https://reactrouter.com/en/main/route/error-element): +If your application is [hoisting pages](../reference/runtime/runtime-class.md#register-an-hoisted-route), it's important to note that they will be rendered outside of the host application's `ModuleErrorBoundary` component. To prevent breaking the entire application when an hoisted page encounters unhandled errors, it is highly recommended to declare a React Router's error boundary for each hoisted page as well, again using [errorElement](https://reactrouter.com/en/main/route/error-element): ```tsx !#9,11 remote-module/src/register.tsx import { type ModuleRegisterFunction, type FireflyRuntime } from "@squide/firefly"; @@ -76,7 +76,7 @@ export const register: ModuleRegisterFunction = runtime => { ## Try it :rocket: -Start the application in a development environment using the `dev` script. Update any of your application routes that is rendered under the newly created error boundary (e.g. that is not hoisted) and throw an `Error`. The error should be handled by the `RootErrorBoundary` component instead of breaking the whole application. +Start the application in a development environment using the `dev` script. Update any of your application routes that is rendered under the newly created error boundary (e.g. that is not hoisted) and throw an `Error`. The error should be handled by the `ModuleErrorBoundary` component instead of breaking the whole application. ### Troubleshoot issues diff --git a/docs/guides/migrate-to-firefly-v9.md b/docs/guides/migrate-to-firefly-v9.md index 2e3cc9df5..ef0ee1421 100644 --- a/docs/guides/migrate-to-firefly-v9.md +++ b/docs/guides/migrate-to-firefly-v9.md @@ -216,15 +216,11 @@ export function App() { router={createBrowserRouter([ { element: rootRoute, + errorElement: , children: [ { - errorElement: , - children: [ - { - element: , - children: registeredRoutes - } - ] + element: , + children: registeredRoutes } ] } @@ -342,7 +338,7 @@ export const registerHost: ModuleRegisterFunction = runtime => { Now: -```tsx !#12 +```tsx !#10 export function App() { return ( @@ -352,12 +348,8 @@ export function App() { router={createBrowserRouter([ { element: rootRoute, - children: [ - { - errorElement: , - children: registeredRoutes - } - ] + errorElement: , + children: registeredRoutes } ])} {...routerProviderProps} @@ -375,3 +367,7 @@ The changes in `v9` have minimal impact on module code. To migrate an existing m 1. Convert all deferred routes into static routes. [View example](#removed-support-for-deferred-routes) 2. Add a `$key` option to the navigation item registrations. [View example](#new-key-option-for-navigation-items) + +### Isolated development + +If your module is set up for [isolated development](../guides/develop-a-module-in-isolation.md), ensure that you also apply the [host application migration steps](#migrate-an-host-application) to your isolated setup. diff --git a/docs/reference/routing/appRouter.md b/docs/reference/routing/appRouter.md index 9fece5682..84bf806c9 100644 --- a/docs/reference/routing/appRouter.md +++ b/docs/reference/routing/appRouter.md @@ -120,7 +120,7 @@ export function RootErrorBoundary() { } ``` -```tsx !#16 host/src/App.tsx +```tsx !#14 host/src/App.tsx import { AppRouter } from "@squide/firefly"; import { RouterProvider, createBrowserRouter } from "react-router-dom"; import { RootErrorBoundary } from "./RootErrorBoundary.tsx"; @@ -134,12 +134,8 @@ export function App() { router={createBrowserRouter([ { element: rootRoute, - children: [ - { - errorElement: , - children: registeredRoutes - } - ] + errorElement: , + children: registeredRoutes } ])} {...routerProviderProps} diff --git a/docs/reference/routing/protectedRoutes.md b/docs/reference/routing/protectedRoutes.md index 2c4a7f2e6..bc84de749 100644 --- a/docs/reference/routing/protectedRoutes.md +++ b/docs/reference/routing/protectedRoutes.md @@ -24,22 +24,15 @@ None The route defining the `ProtectedRoutes` placeholder must be [hoisted](../runtime/runtime-class.md#register-an-hoisted-route); otherwise, there will be an infinite loop as the `ProtectedRoutes` placeholder will render within itself. -```tsx !#13,18 shell/src/register.tsx +```tsx !#8,11 shell/src/register.tsx import { ProtectedRoutes } from "@squide/firefly"; import { RootLayout } from "./RootLayout.tsx"; -import { RootErrorBoundary } from "./RootErrorBoundary.tsx"; runtime.registerRoute({ // Pathless route to declare a root layout. element: , children: [ - { - // Pathless route to declare a root error boundary. - errorElement: , - children: [ - ProtectedRoutes - ] - } + ProtectedRoutes ] }, { hoist: true diff --git a/docs/reference/routing/publicRoutes.md b/docs/reference/routing/publicRoutes.md index 1b58aaee0..58a60e2ea 100644 --- a/docs/reference/routing/publicRoutes.md +++ b/docs/reference/routing/publicRoutes.md @@ -24,22 +24,15 @@ None The route defining the `PublicRoutes` placeholder must be [hoisted](../runtime/runtime-class.md#register-an-hoisted-route); otherwise, there will be an infinite loop as the `PublicRoutes` placeholder will render within itself. -```tsx !#13,18 shell/src/register.tsx +```tsx !#8,11 shell/src/register.tsx import { PublicRoutes } from "@squide/firefly"; import { RootLayout } from "./RootLayout.tsx"; -import { RootErrorBoundary } from "./RootErrorBoundary.tsx"; runtime.registerRoute({ // Pathless route to declare a root layout. element: , children: [ - { - // Pathless route to declare a root error boundary. - errorElement: , - children: [ - PublicRoutes - ] - } + PublicRoutes ] }, { hoist: true diff --git a/docs/reference/tanstack-query/useProtectedDataQueries.md b/docs/reference/tanstack-query/useProtectedDataQueries.md index cd37afebc..3c2078023 100644 --- a/docs/reference/tanstack-query/useProtectedDataQueries.md +++ b/docs/reference/tanstack-query/useProtectedDataQueries.md @@ -188,7 +188,7 @@ export function RootErrorBoundary() { } ``` -```tsx !#58 host/src/App.tsx +```tsx !#55 host/src/App.tsx import { useProtectedDataQueries, useIsBootstrapping, AppRouter } from "@squide/firefly"; import { createBrowserRouter, RouterProvider } from "react-router-dom"; import { ApiError, SessionContext, type Session } from "@sample/shared"; @@ -243,10 +243,10 @@ export function App() { router={createBrowserRouter([ { element: rootRoute, + errorElement: , children: [ { element: , - errorElement: children: registeredRoutes } ] diff --git a/docs/reference/tanstack-query/usePublicDataQueries.md b/docs/reference/tanstack-query/usePublicDataQueries.md index a3d0a3484..614793191 100644 --- a/docs/reference/tanstack-query/usePublicDataQueries.md +++ b/docs/reference/tanstack-query/usePublicDataQueries.md @@ -159,7 +159,7 @@ export function RootErrorBoundary() { } ``` -```tsx !#48 host/src/App.tsx +```tsx !#45 host/src/App.tsx import { usePublicDataQueries, useIsBootstrapping, AppRouter } from "@squide/firefly"; import { createBrowserRouter, RouterProvider } from "react-router-dom"; import { ApiError, FeatureFlagsContext, type FeatureFlags } from "@sample/shared"; @@ -204,10 +204,10 @@ export function App() { router={createBrowserRouter([ { element: rootRoute, + errorElement: , children: [ { element: , - errorElement: children: registeredRoutes } ] diff --git a/packages/firefly/src/useProtectedDataQueries.ts b/packages/firefly/src/useProtectedDataQueries.ts index 59add693d..45e8aba69 100644 --- a/packages/firefly/src/useProtectedDataQueries.ts +++ b/packages/firefly/src/useProtectedDataQueries.ts @@ -23,9 +23,9 @@ export function useProtectedDataQueries>(queries: QueriesOp data: results.map(x => x.data) as MapUseQueryResultToData>, errors, hasErrors: errors.length > 0, - isReady: results.length === queries.length && results.every(x => x.data) + isReady: !results.some(x => x.isPending) }; - }, [queries.length]); + }, []); const { data, errors: queriesErrors, hasErrors, isReady } = useQueries({ queries: queries.map(x => ({ diff --git a/samples/basic/shell/src/AppRouter.tsx b/samples/basic/shell/src/AppRouter.tsx index c27867542..95698ab2b 100644 --- a/samples/basic/shell/src/AppRouter.tsx +++ b/samples/basic/shell/src/AppRouter.tsx @@ -1,5 +1,5 @@ import { SessionManagerContext, useToastListener } from "@basic/shared"; -import { AppRouter as FireflyAppRouter, useIsBootstrapping } from "@squide/firefly"; +import { AppRouter as FireflyAppRouter, useIsBootstrapping, useLogger } from "@squide/firefly"; import { useCallback } from "react"; import { Outlet, RouterProvider, createBrowserRouter } from "react-router-dom"; import { Loading } from "./Loading.tsx"; @@ -32,18 +32,22 @@ function BootstrappingRoute() { } export function AppRouter() { + const logger = useLogger(); + return ( {({ rootRoute, registeredRoutes, routerProviderProps }) => { + logger.debug("[shell] React Router will be rendered with the following route definitions: ", registeredRoutes); + return ( , children: [ { element: , - errorElement: , children: registeredRoutes } ] diff --git a/samples/endpoints/shell/src/AppRouter.tsx b/samples/endpoints/shell/src/AppRouter.tsx index 41a671b46..985d79f21 100644 --- a/samples/endpoints/shell/src/AppRouter.tsx +++ b/samples/endpoints/shell/src/AppRouter.tsx @@ -106,6 +106,8 @@ export interface AppRouterProps { } export function AppRouter(props: AppRouterProps) { + const logger = useLogger(); + const { waitForMsw, telemetryService @@ -114,15 +116,17 @@ export function AppRouter(props: AppRouterProps) { return ( {({ rootRoute, registeredRoutes, routerProviderProps }) => { + logger.debug("[shell] React Router will be rendered with the following route definitions: ", registeredRoutes); + return ( , children: [ { element: , - errorElement: , children: registeredRoutes } ] diff --git a/templates/getting-started/apps/host/src/NotFoundPage.tsx b/templates/getting-started/apps/host/src/NotFoundPage.tsx new file mode 100644 index 000000000..bca1b04cd --- /dev/null +++ b/templates/getting-started/apps/host/src/NotFoundPage.tsx @@ -0,0 +1,5 @@ +export function NotFoundPage() { + return ( +
Not found! Please try another page.
+ ); +} diff --git a/templates/getting-started/apps/host/src/register.tsx b/templates/getting-started/apps/host/src/register.tsx index 2f38b3875..8efd3abd3 100644 --- a/templates/getting-started/apps/host/src/register.tsx +++ b/templates/getting-started/apps/host/src/register.tsx @@ -1,5 +1,6 @@ import { ProtectedRoutes, PublicRoutes, type FireflyRuntime, type ModuleRegisterFunction } from "@squide/firefly"; import { HomePage } from "./HomePage.tsx"; +import { NotFoundPage } from "./NotFoundPage.tsx"; import { RootLayout } from "./RootLayout.tsx"; export const registerHost: ModuleRegisterFunction = runtime => { @@ -16,6 +17,11 @@ export const registerHost: ModuleRegisterFunction = runtime => { hoist: true }); + runtime.registerPublicRoute({ + path: "*", + element: + }); + runtime.registerRoute({ index: true, element: