diff --git a/.changeset/flat-rivers-smell.md b/.changeset/flat-rivers-smell.md new file mode 100644 index 0000000000..386b22525a --- /dev/null +++ b/.changeset/flat-rivers-smell.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs': patch +--- + +Fixed an issue where `` would re-render infinitely under certain conditions. diff --git a/plugins/techdocs/src/reader/components/useReaderState.test.tsx b/plugins/techdocs/src/reader/components/useReaderState.test.tsx index 60898ab288..cfa7d76ba1 100644 --- a/plugins/techdocs/src/reader/components/useReaderState.test.tsx +++ b/plugins/techdocs/src/reader/components/useReaderState.test.tsx @@ -687,5 +687,43 @@ describe('useReaderState', () => { expect.any(Function), ); }); + + it('should return the same data if re-rendered', async () => { + techdocsStorageApi.getEntityDocs.mockResolvedValue('my content'); + techdocsStorageApi.syncEntityDocs.mockImplementation(async () => { + return 'cached'; + }); + + const { result, rerender } = renderHook( + () => useReaderState('Component', 'default', 'backstage', '/example'), + { wrapper: Wrapper }, + ); + + expect(result.current).toEqual({ + state: 'CHECKING', + path: '/example', + content: undefined, + contentErrorMessage: undefined, + syncErrorMessage: undefined, + buildLog: [], + contentReload: expect.any(Function), + }); + + await waitFor(() => { + expect(result.current).toEqual({ + state: 'CONTENT_FRESH', + path: '/example', + content: 'my content', + contentErrorMessage: undefined, + syncErrorMessage: undefined, + buildLog: [], + contentReload: expect.any(Function), + }); + }); + const firstResult = result.current; + + rerender(); + expect(result.current).toBe(firstResult); + }); }); }); diff --git a/plugins/techdocs/src/reader/components/useReaderState.ts b/plugins/techdocs/src/reader/components/useReaderState.ts index 251ae6e3d5..459c6a3728 100644 --- a/plugins/techdocs/src/reader/components/useReaderState.ts +++ b/plugins/techdocs/src/reader/components/useReaderState.ts @@ -343,13 +343,24 @@ export function useReaderState( [state.activeSyncState, state.content, state.contentLoading], ); - return { - state: displayState, - contentReload, - path: state.path, - content: state.content, - contentErrorMessage: state.contentError?.toString(), - syncErrorMessage: state.syncError?.toString(), - buildLog: state.buildLog, - }; + return useMemo( + () => ({ + state: displayState, + contentReload, + path: state.path, + content: state.content, + contentErrorMessage: state.contentError?.toString(), + syncErrorMessage: state.syncError?.toString(), + buildLog: state.buildLog, + }), + [ + displayState, + contentReload, + state.path, + state.content, + state.contentError, + state.syncError, + state.buildLog, + ], + ); }