Skip to content
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

fix: useProtectedDataQueries isReady value #197

Merged
merged 2 commits into from
Sep 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/tricky-moons-dress.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@squide/firefly": patch
---

Fix useProtectedDataQueries isReady result value.
2 changes: 1 addition & 1 deletion docs/guides/add-authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
10 changes: 6 additions & 4 deletions docs/guides/develop-a-module-in-isolation.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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 (
Expand All @@ -64,6 +65,7 @@ export function FireflyAppRouter() {
router={createBrowserRouter([
{
element: rootRoute,
errorElement: <RootErrorBoundary />
children: registeredRoutes
}
])}
Expand All @@ -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<FireflyRuntime> = runtime => {
runtime.registerRoute({
element: <RootLayout />,
children: [
{
errorElement: <RootErrorBoundary />,
errorElement: <ModuleErrorBoundary />,
children: [
PublicRoutes,
ProtectedRoutes
Expand All @@ -102,7 +104,7 @@ export const registerShell: ModuleRegisterFunction<FireflyRuntime> = 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
Expand Down
18 changes: 9 additions & 9 deletions docs/guides/isolate-module-failures.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<div>An error occured while rendering a page from a module!</div>
)
Expand All @@ -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<FireflyRuntime> = runtime => {
runtime.registerRoute({
element: <RootLayout />,
children: [
{
// Default error boundary.
errorElement: <RootErrorBoundary />,
// Error boundary for modules.
errorElement: <ModuleErrorBoundary />,
children: [
PublicRoutes,
ProtectedRoutes
Expand All @@ -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";
Expand All @@ -76,7 +76,7 @@ export const register: ModuleRegisterFunction<FireflyRuntime> = 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

Expand Down
24 changes: 10 additions & 14 deletions docs/guides/migrate-to-firefly-v9.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,15 +216,11 @@ export function App() {
router={createBrowserRouter([
{
element: rootRoute,
errorElement: <RootErrorBoundary />,
children: [
{
errorElement: <RootErrorBoundary />,
children: [
{
element: <BootstrappingRoute />,
children: registeredRoutes
}
]
element: <BootstrappingRoute />,
children: registeredRoutes
}
]
}
Expand Down Expand Up @@ -342,7 +338,7 @@ export const registerHost: ModuleRegisterFunction<FireflyRuntime> = runtime => {

Now:

```tsx !#12
```tsx !#10
export function App() {
return (
<AppRouter waitForMsw>
Expand All @@ -352,12 +348,8 @@ export function App() {
router={createBrowserRouter([
{
element: rootRoute,
children: [
{
errorElement: <RootErrorBoundary />,
children: registeredRoutes
}
]
errorElement: <RootErrorBoundary />,
children: registeredRoutes
}
])}
{...routerProviderProps}
Expand All @@ -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.
10 changes: 3 additions & 7 deletions docs/reference/routing/appRouter.md
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -134,12 +134,8 @@ export function App() {
router={createBrowserRouter([
{
element: rootRoute,
children: [
{
errorElement: <RootErrorBoundary />,
children: registeredRoutes
}
]
errorElement: <RootErrorBoundary />,
children: registeredRoutes
}
])}
{...routerProviderProps}
Expand Down
11 changes: 2 additions & 9 deletions docs/reference/routing/protectedRoutes.md
Original file line number Diff line number Diff line change
Expand Up @@ -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: <RootLayout />,
children: [
{
// Pathless route to declare a root error boundary.
errorElement: <RootErrorBoundary />,
children: [
ProtectedRoutes
]
}
ProtectedRoutes
]
}, {
hoist: true
Expand Down
11 changes: 2 additions & 9 deletions docs/reference/routing/publicRoutes.md
Original file line number Diff line number Diff line change
Expand Up @@ -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: <RootLayout />,
children: [
{
// Pathless route to declare a root error boundary.
errorElement: <RootErrorBoundary />,
children: [
PublicRoutes
]
}
PublicRoutes
]
}, {
hoist: true
Expand Down
4 changes: 2 additions & 2 deletions docs/reference/tanstack-query/useProtectedDataQueries.md
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -243,10 +243,10 @@ export function App() {
router={createBrowserRouter([
{
element: rootRoute,
errorElement: <RootErrorBoundary />,
children: [
{
element: <BootstrappingRoute />,
errorElement: <RootErrorBoundary />
children: registeredRoutes
}
]
Expand Down
4 changes: 2 additions & 2 deletions docs/reference/tanstack-query/usePublicDataQueries.md
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -204,10 +204,10 @@ export function App() {
router={createBrowserRouter([
{
element: rootRoute,
errorElement: <RootErrorBoundary />,
children: [
{
element: <BootstrappingRoute />,
errorElement: <RootErrorBoundary />
children: registeredRoutes
}
]
Expand Down
4 changes: 2 additions & 2 deletions packages/firefly/src/useProtectedDataQueries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ export function useProtectedDataQueries<T extends Array<any>>(queries: QueriesOp
data: results.map(x => x.data) as MapUseQueryResultToData<QueriesResults<T>>,
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 => ({
Expand Down
8 changes: 6 additions & 2 deletions samples/basic/shell/src/AppRouter.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand Down Expand Up @@ -32,18 +32,22 @@ function BootstrappingRoute() {
}

export function AppRouter() {
const logger = useLogger();

return (
<FireflyAppRouter waitForMsw={false}>
{({ rootRoute, registeredRoutes, routerProviderProps }) => {
logger.debug("[shell] React Router will be rendered with the following route definitions: ", registeredRoutes);

return (
<RouterProvider
router={createBrowserRouter([
{
element: rootRoute,
errorElement: <RootErrorBoundary />,
children: [
{
element: <BootstrappingRoute />,
errorElement: <RootErrorBoundary />,
children: registeredRoutes
}
]
Expand Down
6 changes: 5 additions & 1 deletion samples/endpoints/shell/src/AppRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ export interface AppRouterProps {
}

export function AppRouter(props: AppRouterProps) {
const logger = useLogger();

const {
waitForMsw,
telemetryService
Expand All @@ -114,15 +116,17 @@ export function AppRouter(props: AppRouterProps) {
return (
<FireflyAppRouter waitForMsw={waitForMsw} waitForPublicData waitForProtectedData>
{({ rootRoute, registeredRoutes, routerProviderProps }) => {
logger.debug("[shell] React Router will be rendered with the following route definitions: ", registeredRoutes);

return (
<RouterProvider
router={createBrowserRouter([
{
element: rootRoute,
errorElement: <RootErrorBoundary />,
children: [
{
element: <BootstrappingRoute telemetryService={telemetryService} />,
errorElement: <RootErrorBoundary />,
children: registeredRoutes
}
]
Expand Down
5 changes: 5 additions & 0 deletions templates/getting-started/apps/host/src/NotFoundPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export function NotFoundPage() {
return (
<div>Not found! Please try another page.</div>
);
}
6 changes: 6 additions & 0 deletions templates/getting-started/apps/host/src/register.tsx
Original file line number Diff line number Diff line change
@@ -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<FireflyRuntime> = runtime => {
Expand All @@ -16,6 +17,11 @@ export const registerHost: ModuleRegisterFunction<FireflyRuntime> = runtime => {
hoist: true
});

runtime.registerPublicRoute({
path: "*",
element: <NotFoundPage />
});

runtime.registerRoute({
index: true,
element: <HomePage />
Expand Down