core-app-api: added app.createRoot()

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2022-12-08 00:04:15 +01:00
parent 06d65c51b2
commit 62e58de887
4 changed files with 90 additions and 1 deletions
+46 -1
View File
@@ -2,4 +2,49 @@
'@backstage/core-app-api': minor
---
Added a new `AppRouter` component that replaces the same component currently created through `app.getRouter()`.
Added a new `AppRouter` component and `app.createRoot()` method that replaces `app.getRouter()` and `app.getProvider()`, which are now deprecated. The new `AppRouter` component is a drop-in replacement for the old router component, while the new `app.createRoot()` method is used instead of the old provider component.
An old app setup might look like this:
```tsx
const app = createApp(/* ... */);
const AppProvider = app.getProvider();
const AppRouter = app.getRouter();
const routes = ...;
const App = () => (
<AppProvider>
<AlertDisplay />
<OAuthRequestDialog />
<AppRouter>
<Root>{routes}</Root>
</AppRouter>
</AppProvider>
);
export default App;
```
With these new APIs, the setup now looks like this:
```tsx
import { AppRouter } from '@backstage/core-app-api';
const app = createApp(/* ... */);
const routes = ...;
export default app.createRoot(
<>
<AlertDisplay />
<OAuthRequestDialog />
<AppRouter>
<Root>{routes}</Root>
</AppRouter>
</>,
);
```
Note that `app.createRoot()` accepts a React element, rather than a component.
+1
View File
@@ -262,6 +262,7 @@ export type AuthApiCreateOptions = {
export type BackstageApp = {
getPlugins(): BackstagePlugin[];
getSystemIcon(key: string): IconComponent | undefined;
createRoot(element: JSX.Element): ComponentType<{}>;
getProvider(): ComponentType<{}>;
getRouter(): ComponentType<{}>;
};
@@ -248,7 +248,23 @@ export class AppManager implements BackstageApp {
return this.components;
}
createRoot(element: JSX.Element): ComponentType<{}> {
const AppProvider = this.getProvider();
const AppRoot = () => {
return <AppProvider>{element}</AppProvider>;
};
return AppRoot;
}
#getProviderCalled = false;
getProvider(): ComponentType<{}> {
if (this.#getProviderCalled) {
throw new Error(
'app.getProvider() or app.createRoot() has already been called, and can only be called once',
);
}
this.#getProviderCalled = true;
const appContext = new AppContextImpl(this);
// We only validate routes once
+27
View File
@@ -298,9 +298,36 @@ export type BackstageApp = {
*/
getSystemIcon(key: string): IconComponent | undefined;
/**
* Creates the root component that renders the entire app.
*
* @remarks
*
* This method must only be called once, and you have to provide it the entire
* app element tree. The element tree will be analyzed to discover plugins,
* routes, and other app features. The returned component will render all
* of the app elements wrapped within the app context provider.
*
* @example
* ```tsx
* export default app.createRoot(
* <>
* <AlertDisplay />
* <OAuthRequestDialog />
* <AppRouter>
* <Root>{routes}</Root>
* </AppRouter>
* </>,
* );
* ```
*/
createRoot(element: JSX.Element): ComponentType<{}>;
/**
* Provider component that should wrap the Router created with getRouter()
* and any other components that need to be within the app context.
*
* @deprecated Use {@link BackstageApp.createRoot} instead.
*/
getProvider(): ComponentType<{}>;