@backstage/plugin-catalog-react 3.2.1-next.2 → 3.2.1-next.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @backstage/plugin-catalog-react
2
2
 
3
+ ## 3.2.1-next.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 9fcfbc9: Fixed a performance issue where all components reading the entity context on an entity page would rerender unnecessarily whenever the page rendered again without the entity data having changed, for example when a URL query parameter changed. This was particularly noticeable when switching tabs in the entity inspector dialog, which caused the entire underlying page to rerender.
8
+ - Updated dependencies
9
+ - @backstage/core-plugin-api@1.12.9-next.1
10
+
3
11
  ## 3.2.1-next.2
4
12
 
5
13
  ### Patch Changes
@@ -2,22 +2,24 @@ import { jsx } from 'react/jsx-runtime';
2
2
  import { stringifyEntityRef } from '@backstage/catalog-model';
3
3
  import { AnalyticsContext } from '@backstage/core-plugin-api';
4
4
  import { createVersionedContext, createVersionedValueMap, useVersionedContext } from '@backstage/version-bridge';
5
+ import { useMemo } from 'react';
5
6
 
6
7
  const NewEntityContext = createVersionedContext(
7
8
  "entity-context"
8
9
  );
9
10
  const AsyncEntityProvider = (props) => {
10
11
  const { children, entity, loading, error, refresh } = props;
11
- const value = { entity, loading, error, refresh };
12
- return /* @__PURE__ */ jsx(NewEntityContext.Provider, { value: createVersionedValueMap({ 1: value }), children: /* @__PURE__ */ jsx(
13
- AnalyticsContext,
14
- {
15
- attributes: {
16
- ...entity ? { entityRef: stringifyEntityRef(entity) } : void 0
17
- },
18
- children
19
- }
20
- ) });
12
+ const value = useMemo(
13
+ () => createVersionedValueMap({ 1: { entity, loading, error, refresh } }),
14
+ [entity, loading, error, refresh]
15
+ );
16
+ const attributes = useMemo(
17
+ () => ({
18
+ ...entity ? { entityRef: stringifyEntityRef(entity) } : void 0
19
+ }),
20
+ [entity]
21
+ );
22
+ return /* @__PURE__ */ jsx(NewEntityContext.Provider, { value, children: /* @__PURE__ */ jsx(AnalyticsContext, { attributes, children }) });
21
23
  };
22
24
  const EntityProvider = (props) => /* @__PURE__ */ jsx(
23
25
  AsyncEntityProvider,
@@ -1 +1 @@
1
- {"version":3,"file":"useEntity.esm.js","sources":["../../src/hooks/useEntity.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { Entity, stringifyEntityRef } from '@backstage/catalog-model';\nimport { AnalyticsContext } from '@backstage/core-plugin-api';\nimport {\n createVersionedContext,\n createVersionedValueMap,\n useVersionedContext,\n} from '@backstage/version-bridge';\nimport { ReactNode } from 'react';\n\n/** @public */\nexport type EntityLoadingStatus<TEntity extends Entity = Entity> = {\n entity?: TEntity;\n loading: boolean;\n error?: Error;\n refresh?: VoidFunction;\n};\n\n// This context has support for multiple concurrent versions of this package.\n// It is currently used in parallel with the old context in order to provide\n// a smooth transition, but will eventually be the only context we use.\nconst NewEntityContext = createVersionedContext<{ 1: EntityLoadingStatus }>(\n 'entity-context',\n);\n\n/**\n * Properties for the AsyncEntityProvider component.\n *\n * @public\n */\nexport interface AsyncEntityProviderProps {\n children: ReactNode;\n entity?: Entity;\n loading: boolean;\n error?: Error;\n refresh?: VoidFunction;\n}\n\n/**\n * Provides a loaded entity to be picked up by the `useEntity` hook.\n *\n * @public\n */\nexport const AsyncEntityProvider = (props: AsyncEntityProviderProps) => {\n const { children, entity, loading, error, refresh } = props;\n const value = { entity, loading, error, refresh };\n // We provide both the old and the new context, since\n // consumers might be doing things like `useContext(EntityContext)`\n return (\n <NewEntityContext.Provider value={createVersionedValueMap({ 1: value })}>\n <AnalyticsContext\n attributes={{\n ...(entity ? { entityRef: stringifyEntityRef(entity) } : undefined),\n }}\n >\n {children}\n </AnalyticsContext>\n </NewEntityContext.Provider>\n );\n};\n\n/**\n * Properties for the EntityProvider component.\n *\n * @public\n */\nexport interface EntityProviderProps {\n children: ReactNode;\n entity?: Entity;\n}\n\n/**\n * Provides an entity to be picked up by the `useEntity` hook.\n *\n * @public\n */\nexport const EntityProvider = (props: EntityProviderProps) => (\n <AsyncEntityProvider\n entity={props.entity}\n loading={!Boolean(props.entity)}\n error={undefined}\n refresh={undefined}\n children={props.children}\n />\n);\n\n/**\n * Grab the current entity from the context, throws if the entity has not yet been loaded\n * or is not available.\n *\n * @public\n */\nexport function useEntity<TEntity extends Entity = Entity>(): {\n entity: TEntity;\n} {\n const versionedHolder = useVersionedContext<{ 1: EntityLoadingStatus }>(\n 'entity-context',\n );\n\n if (!versionedHolder) {\n throw new Error('Entity context is not available');\n }\n\n const value = versionedHolder.atVersion(1);\n if (!value) {\n throw new Error('EntityContext v1 not available');\n }\n\n if (!value.entity) {\n throw new Error(\n 'useEntity hook is being called outside of an EntityLayout where the entity has not been loaded. If this is intentional, please use useAsyncEntity instead.',\n );\n }\n\n return { entity: value.entity as TEntity };\n}\n\n/**\n * Grab the current entity from the context, provides loading state and errors, and the ability to refresh.\n *\n * @public\n */\nexport function useAsyncEntity<\n TEntity extends Entity = Entity,\n>(): EntityLoadingStatus<TEntity> {\n const versionedHolder = useVersionedContext<{ 1: EntityLoadingStatus }>(\n 'entity-context',\n );\n\n if (!versionedHolder) {\n throw new Error('Entity context is not available');\n }\n const value = versionedHolder.atVersion(1);\n if (!value) {\n throw new Error('EntityContext v1 not available');\n }\n\n const { entity, loading, error, refresh } = value;\n return { entity: entity as TEntity, loading, error, refresh };\n}\n"],"names":[],"mappings":";;;;;AAmCA,MAAM,gBAAA,GAAmB,sBAAA;AAAA,EACvB;AACF,CAAA;AAoBO,MAAM,mBAAA,GAAsB,CAAC,KAAA,KAAoC;AACtE,EAAA,MAAM,EAAE,QAAA,EAAU,MAAA,EAAQ,OAAA,EAAS,KAAA,EAAO,SAAQ,GAAI,KAAA;AACtD,EAAA,MAAM,KAAA,GAAQ,EAAE,MAAA,EAAQ,OAAA,EAAS,OAAO,OAAA,EAAQ;AAGhD,EAAA,uBACE,GAAA,CAAC,gBAAA,CAAiB,QAAA,EAAjB,EAA0B,KAAA,EAAO,wBAAwB,EAAE,CAAA,EAAG,KAAA,EAAO,CAAA,EACpE,QAAA,kBAAA,GAAA;AAAA,IAAC,gBAAA;AAAA,IAAA;AAAA,MACC,UAAA,EAAY;AAAA,QACV,GAAI,MAAA,GAAS,EAAE,WAAW,kBAAA,CAAmB,MAAM,GAAE,GAAI;AAAA,OAC3D;AAAA,MAEC;AAAA;AAAA,GACH,EACF,CAAA;AAEJ;AAiBO,MAAM,cAAA,GAAiB,CAAC,KAAA,qBAC7B,GAAA;AAAA,EAAC,mBAAA;AAAA,EAAA;AAAA,IACC,QAAQ,KAAA,CAAM,MAAA;AAAA,IACd,OAAA,EAAS,CAAC,OAAA,CAAQ,KAAA,CAAM,MAAM,CAAA;AAAA,IAC9B,KAAA,EAAO,MAAA;AAAA,IACP,OAAA,EAAS,MAAA;AAAA,IACT,UAAU,KAAA,CAAM;AAAA;AAClB;AASK,SAAS,SAAA,GAEd;AACA,EAAA,MAAM,eAAA,GAAkB,mBAAA;AAAA,IACtB;AAAA,GACF;AAEA,EAAA,IAAI,CAAC,eAAA,EAAiB;AACpB,IAAA,MAAM,IAAI,MAAM,iCAAiC,CAAA;AAAA,EACnD;AAEA,EAAA,MAAM,KAAA,GAAQ,eAAA,CAAgB,SAAA,CAAU,CAAC,CAAA;AACzC,EAAA,IAAI,CAAC,KAAA,EAAO;AACV,IAAA,MAAM,IAAI,MAAM,gCAAgC,CAAA;AAAA,EAClD;AAEA,EAAA,IAAI,CAAC,MAAM,MAAA,EAAQ;AACjB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KACF;AAAA,EACF;AAEA,EAAA,OAAO,EAAE,MAAA,EAAQ,KAAA,CAAM,MAAA,EAAkB;AAC3C;AAOO,SAAS,cAAA,GAEkB;AAChC,EAAA,MAAM,eAAA,GAAkB,mBAAA;AAAA,IACtB;AAAA,GACF;AAEA,EAAA,IAAI,CAAC,eAAA,EAAiB;AACpB,IAAA,MAAM,IAAI,MAAM,iCAAiC,CAAA;AAAA,EACnD;AACA,EAAA,MAAM,KAAA,GAAQ,eAAA,CAAgB,SAAA,CAAU,CAAC,CAAA;AACzC,EAAA,IAAI,CAAC,KAAA,EAAO;AACV,IAAA,MAAM,IAAI,MAAM,gCAAgC,CAAA;AAAA,EAClD;AAEA,EAAA,MAAM,EAAE,MAAA,EAAQ,OAAA,EAAS,KAAA,EAAO,SAAQ,GAAI,KAAA;AAC5C,EAAA,OAAO,EAAE,MAAA,EAA2B,OAAA,EAAS,KAAA,EAAO,OAAA,EAAQ;AAC9D;;;;"}
1
+ {"version":3,"file":"useEntity.esm.js","sources":["../../src/hooks/useEntity.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { Entity, stringifyEntityRef } from '@backstage/catalog-model';\nimport { AnalyticsContext } from '@backstage/core-plugin-api';\nimport {\n createVersionedContext,\n createVersionedValueMap,\n useVersionedContext,\n} from '@backstage/version-bridge';\nimport { ReactNode, useMemo } from 'react';\n\n/** @public */\nexport type EntityLoadingStatus<TEntity extends Entity = Entity> = {\n entity?: TEntity;\n loading: boolean;\n error?: Error;\n refresh?: VoidFunction;\n};\n\n// This context has support for multiple concurrent versions of this package.\n// It is currently used in parallel with the old context in order to provide\n// a smooth transition, but will eventually be the only context we use.\nconst NewEntityContext = createVersionedContext<{ 1: EntityLoadingStatus }>(\n 'entity-context',\n);\n\n/**\n * Properties for the AsyncEntityProvider component.\n *\n * @public\n */\nexport interface AsyncEntityProviderProps {\n children: ReactNode;\n entity?: Entity;\n loading: boolean;\n error?: Error;\n refresh?: VoidFunction;\n}\n\n/**\n * Provides a loaded entity to be picked up by the `useEntity` hook.\n *\n * @public\n */\nexport const AsyncEntityProvider = (props: AsyncEntityProviderProps) => {\n const { children, entity, loading, error, refresh } = props;\n // The versioned value is memoized so that context consumers don't get\n // rerendered when this provider rerenders without any of the actual\n // values changing, e.g. as a result of URL query parameter updates.\n const value = useMemo(\n () => createVersionedValueMap({ 1: { entity, loading, error, refresh } }),\n [entity, loading, error, refresh],\n );\n const attributes = useMemo(\n () => ({\n ...(entity ? { entityRef: stringifyEntityRef(entity) } : undefined),\n }),\n [entity],\n );\n return (\n <NewEntityContext.Provider value={value}>\n <AnalyticsContext attributes={attributes}>{children}</AnalyticsContext>\n </NewEntityContext.Provider>\n );\n};\n\n/**\n * Properties for the EntityProvider component.\n *\n * @public\n */\nexport interface EntityProviderProps {\n children: ReactNode;\n entity?: Entity;\n}\n\n/**\n * Provides an entity to be picked up by the `useEntity` hook.\n *\n * @public\n */\nexport const EntityProvider = (props: EntityProviderProps) => (\n <AsyncEntityProvider\n entity={props.entity}\n loading={!Boolean(props.entity)}\n error={undefined}\n refresh={undefined}\n children={props.children}\n />\n);\n\n/**\n * Grab the current entity from the context, throws if the entity has not yet been loaded\n * or is not available.\n *\n * @public\n */\nexport function useEntity<TEntity extends Entity = Entity>(): {\n entity: TEntity;\n} {\n const versionedHolder = useVersionedContext<{ 1: EntityLoadingStatus }>(\n 'entity-context',\n );\n\n if (!versionedHolder) {\n throw new Error('Entity context is not available');\n }\n\n const value = versionedHolder.atVersion(1);\n if (!value) {\n throw new Error('EntityContext v1 not available');\n }\n\n if (!value.entity) {\n throw new Error(\n 'useEntity hook is being called outside of an EntityLayout where the entity has not been loaded. If this is intentional, please use useAsyncEntity instead.',\n );\n }\n\n return { entity: value.entity as TEntity };\n}\n\n/**\n * Grab the current entity from the context, provides loading state and errors, and the ability to refresh.\n *\n * @public\n */\nexport function useAsyncEntity<\n TEntity extends Entity = Entity,\n>(): EntityLoadingStatus<TEntity> {\n const versionedHolder = useVersionedContext<{ 1: EntityLoadingStatus }>(\n 'entity-context',\n );\n\n if (!versionedHolder) {\n throw new Error('Entity context is not available');\n }\n const value = versionedHolder.atVersion(1);\n if (!value) {\n throw new Error('EntityContext v1 not available');\n }\n\n const { entity, loading, error, refresh } = value;\n return { entity: entity as TEntity, loading, error, refresh };\n}\n"],"names":[],"mappings":";;;;;;AAmCA,MAAM,gBAAA,GAAmB,sBAAA;AAAA,EACvB;AACF,CAAA;AAoBO,MAAM,mBAAA,GAAsB,CAAC,KAAA,KAAoC;AACtE,EAAA,MAAM,EAAE,QAAA,EAAU,MAAA,EAAQ,OAAA,EAAS,KAAA,EAAO,SAAQ,GAAI,KAAA;AAItD,EAAA,MAAM,KAAA,GAAQ,OAAA;AAAA,IACZ,MAAM,uBAAA,CAAwB,EAAE,CAAA,EAAG,EAAE,QAAQ,OAAA,EAAS,KAAA,EAAO,OAAA,EAAQ,EAAG,CAAA;AAAA,IACxE,CAAC,MAAA,EAAQ,OAAA,EAAS,KAAA,EAAO,OAAO;AAAA,GAClC;AACA,EAAA,MAAM,UAAA,GAAa,OAAA;AAAA,IACjB,OAAO;AAAA,MACL,GAAI,MAAA,GAAS,EAAE,WAAW,kBAAA,CAAmB,MAAM,GAAE,GAAI;AAAA,KAC3D,CAAA;AAAA,IACA,CAAC,MAAM;AAAA,GACT;AACA,EAAA,uBACE,GAAA,CAAC,iBAAiB,QAAA,EAAjB,EAA0B,OACzB,QAAA,kBAAA,GAAA,CAAC,gBAAA,EAAA,EAAiB,UAAA,EAAyB,QAAA,EAAS,CAAA,EACtD,CAAA;AAEJ;AAiBO,MAAM,cAAA,GAAiB,CAAC,KAAA,qBAC7B,GAAA;AAAA,EAAC,mBAAA;AAAA,EAAA;AAAA,IACC,QAAQ,KAAA,CAAM,MAAA;AAAA,IACd,OAAA,EAAS,CAAC,OAAA,CAAQ,KAAA,CAAM,MAAM,CAAA;AAAA,IAC9B,KAAA,EAAO,MAAA;AAAA,IACP,OAAA,EAAS,MAAA;AAAA,IACT,UAAU,KAAA,CAAM;AAAA;AAClB;AASK,SAAS,SAAA,GAEd;AACA,EAAA,MAAM,eAAA,GAAkB,mBAAA;AAAA,IACtB;AAAA,GACF;AAEA,EAAA,IAAI,CAAC,eAAA,EAAiB;AACpB,IAAA,MAAM,IAAI,MAAM,iCAAiC,CAAA;AAAA,EACnD;AAEA,EAAA,MAAM,KAAA,GAAQ,eAAA,CAAgB,SAAA,CAAU,CAAC,CAAA;AACzC,EAAA,IAAI,CAAC,KAAA,EAAO;AACV,IAAA,MAAM,IAAI,MAAM,gCAAgC,CAAA;AAAA,EAClD;AAEA,EAAA,IAAI,CAAC,MAAM,MAAA,EAAQ;AACjB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KACF;AAAA,EACF;AAEA,EAAA,OAAO,EAAE,MAAA,EAAQ,KAAA,CAAM,MAAA,EAAkB;AAC3C;AAOO,SAAS,cAAA,GAEkB;AAChC,EAAA,MAAM,eAAA,GAAkB,mBAAA;AAAA,IACtB;AAAA,GACF;AAEA,EAAA,IAAI,CAAC,eAAA,EAAiB;AACpB,IAAA,MAAM,IAAI,MAAM,iCAAiC,CAAA;AAAA,EACnD;AACA,EAAA,MAAM,KAAA,GAAQ,eAAA,CAAgB,SAAA,CAAU,CAAC,CAAA;AACzC,EAAA,IAAI,CAAC,KAAA,EAAO;AACV,IAAA,MAAM,IAAI,MAAM,gCAAgC,CAAA;AAAA,EAClD;AAEA,EAAA,MAAM,EAAE,MAAA,EAAQ,OAAA,EAAS,KAAA,EAAO,SAAQ,GAAI,KAAA;AAC5C,EAAA,OAAO,EAAE,MAAA,EAA2B,OAAA,EAAS,KAAA,EAAO,OAAA,EAAQ;AAC9D;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/plugin-catalog-react",
3
- "version": "3.2.1-next.2",
3
+ "version": "3.2.1-next.3",
4
4
  "description": "A frontend library that helps other Backstage plugins interact with the catalog",
5
5
  "backstage": {
6
6
  "role": "web-library",
@@ -77,7 +77,7 @@
77
77
  "@backstage/catalog-model": "1.9.0",
78
78
  "@backstage/core-compat-api": "0.5.14-next.1",
79
79
  "@backstage/core-components": "0.18.13-next.2",
80
- "@backstage/core-plugin-api": "1.12.9-next.0",
80
+ "@backstage/core-plugin-api": "1.12.9-next.1",
81
81
  "@backstage/errors": "1.3.1",
82
82
  "@backstage/filter-predicates": "0.1.4",
83
83
  "@backstage/frontend-plugin-api": "0.18.0-next.0",