diff --git a/docs/suspensive.org/src/content/en/docs/react/lazy.mdx b/docs/suspensive.org/src/content/en/docs/react/lazy.mdx index 4c9ffe8e2..109830795 100644 --- a/docs/suspensive.org/src/content/en/docs/react/lazy.mdx +++ b/docs/suspensive.org/src/content/en/docs/react/lazy.mdx @@ -127,7 +127,7 @@ const VersionSkewSafeComponent = lazy( + retryDelay: 1000, + })) - const Component = lazy(() => import('./Component'), { -- onError: ({ error, load }) => { +- onError: ({ error }) => { - const reloadKey = 'component_reload_count' - const currentCount = parseInt(sessionStorage.getItem(reloadKey) || '0') - const maxRetries = 3 @@ -141,7 +141,7 @@ const VersionSkewSafeComponent = lazy( - }, 1000) - } - }, -- onSuccess: ({ load }) => { +- onSuccess: () => { - // Clear reload count on success - sessionStorage.removeItem('component_reload_count') - }, diff --git a/docs/suspensive.org/src/content/ko/docs/react/lazy.mdx b/docs/suspensive.org/src/content/ko/docs/react/lazy.mdx index 10f4d1c9f..d436922b5 100644 --- a/docs/suspensive.org/src/content/ko/docs/react/lazy.mdx +++ b/docs/suspensive.org/src/content/ko/docs/react/lazy.mdx @@ -127,7 +127,7 @@ const VersionSkewSafeComponent = lazy( + retryDelay: 1000, + })) - const Component = lazy(() => import('./Component'), { -- onError: ({ error, load }) => { +- onError: ({ error }) => { - const reloadKey = 'component_reload_count' - const currentCount = parseInt(sessionStorage.getItem(reloadKey) || '0') - const maxRetries = 3 @@ -141,7 +141,7 @@ const VersionSkewSafeComponent = lazy( - }, 1000) - } - }, -- onSuccess: ({ load }) => { +- onSuccess: () => { - // 성공 시 재시도 횟수 초기화 - sessionStorage.removeItem('component_reload_count') - }, diff --git a/packages/react/src/lazy.spec.tsx b/packages/react/src/lazy.spec.tsx index 5e6b30de7..3d9cbccba 100644 --- a/packages/react/src/lazy.spec.tsx +++ b/packages/react/src/lazy.spec.tsx @@ -317,7 +317,7 @@ describe('lazy', () => { expect(screen.getByText('error')).toBeInTheDocument() expect(onError).toHaveBeenCalledTimes(1) - expect(onError).toHaveBeenCalledWith({ error: expect.any(Error), load: expect.any(Function) }) + expect(onError).toHaveBeenCalledWith(expect.objectContaining({ error: expect.any(Error) })) }) it('should execute component onError first, then default onError', async () => { @@ -615,7 +615,7 @@ describe('lazy', () => { const mockImport = importCache.createImport({ failureCount: 10, failureDelay: 100, successDelay: 50 }) const Component = lazy(() => mockImport('/test-component')) - // Get the load function and set storage to the limit + // Set storage to the limit using the original load's toString (which is the storage key) const loadFunction = Component.load storage.setItem(loadFunction.toString(), '1') @@ -717,7 +717,7 @@ describe('lazy', () => { // Component's onError should also be called expect(individualOnError).toHaveBeenCalledTimes(1) - expect(individualOnError).toHaveBeenCalledWith({ error: expect.any(Error), load: expect.any(Function) }) + expect(individualOnError).toHaveBeenCalledWith(expect.objectContaining({ error: expect.any(Error) })) await act(() => vi.advanceTimersByTimeAsync(1)) expect(mockReload).toHaveBeenCalledTimes(1) }) @@ -749,7 +749,7 @@ describe('lazy', () => { // Component's onError should also be called expect(individualOnError).toHaveBeenCalledTimes(1) - expect(individualOnError).toHaveBeenCalledWith({ error: expect.any(Error), load: expect.any(Function) }) + expect(individualOnError).toHaveBeenCalledWith(expect.objectContaining({ error: expect.any(Error) })) await act(() => vi.advanceTimersByTimeAsync(1)) // reloadOnError should work expect(mockReload).toHaveBeenCalledTimes(1) @@ -788,9 +788,9 @@ describe('lazy', () => { // Factory's onError should also be called expect(defaultOnError).toHaveBeenCalledTimes(1) - expect(defaultOnError).toHaveBeenCalledWith({ error: expect.any(Error), load: expect.any(Function) }) + expect(defaultOnError).toHaveBeenCalledWith(expect.objectContaining({ error: expect.any(Error) })) expect(individualOnError).toHaveBeenCalledTimes(1) - expect(individualOnError).toHaveBeenCalledWith({ error: expect.any(Error), load: expect.any(Function) }) + expect(individualOnError).toHaveBeenCalledWith(expect.objectContaining({ error: expect.any(Error) })) expect(defaultOnSuccess).toHaveBeenCalledTimes(0) expect(individualOnSuccess).toHaveBeenCalledTimes(0) await act(() => vi.advanceTimersByTimeAsync(1)) @@ -815,8 +815,8 @@ describe('lazy', () => { expect(defaultOnError).toHaveBeenCalledTimes(1) expect(individualOnError).toHaveBeenCalledTimes(1) - expect(defaultOnSuccess).toHaveBeenCalledWith({ load: expect.any(Function) }) - expect(individualOnSuccess).toHaveBeenCalledWith({ load: expect.any(Function) }) + expect(defaultOnSuccess).toHaveBeenCalledTimes(1) + expect(individualOnSuccess).toHaveBeenCalledTimes(1) expect(mockReload).toHaveBeenCalledTimes(1) }) }) diff --git a/packages/react/src/lazy.ts b/packages/react/src/lazy.ts index 7a3f8da0e..3d5330286 100644 --- a/packages/react/src/lazy.ts +++ b/packages/react/src/lazy.ts @@ -1,9 +1,11 @@ 'use client' import { type ComponentType, type LazyExoticComponent, lazy as originalLazy } from 'react' +const reloadOnErrorStorageKeySymbol = Symbol('reloadOnErrorStorageKey') + interface LazyOptions { - onSuccess?: ({ load }: { load: () => Promise<{ default: ComponentType }> }) => void - onError?: ({ error, load }: { error: unknown; load: () => Promise<{ default: ComponentType }> }) => void + onSuccess?: () => void + onError?: (options: { error: unknown }) => void } /** @@ -47,14 +49,29 @@ export const createLazy = ): LazyExoticComponent & { load: () => Promise<{ default: T }> } => { + const storageKey = load.toString() + const composedOnSuccess = () => { - options?.onSuccess?.({ load }) - defaultOptions.onSuccess?.({ load }) + ;(options?.onSuccess as undefined | ((arg: { [reloadOnErrorStorageKeySymbol]: string }) => void))?.({ + [reloadOnErrorStorageKeySymbol]: storageKey, + }) + ;(defaultOptions.onSuccess as undefined | ((arg: { [reloadOnErrorStorageKeySymbol]: string }) => void))?.({ + [reloadOnErrorStorageKeySymbol]: storageKey, + }) } const composedOnError = (error: unknown) => { - options?.onError?.({ error, load }) - defaultOptions.onError?.({ error, load }) + ;(options?.onError as undefined | ((arg: { error: unknown; [reloadOnErrorStorageKeySymbol]: string }) => void))?.( + { + error, + [reloadOnErrorStorageKeySymbol]: storageKey, + } + ) + ;( + defaultOptions.onError as + | undefined + | ((arg: { error: unknown; [reloadOnErrorStorageKeySymbol]: string }) => void) + )?.({ error, [reloadOnErrorStorageKeySymbol]: storageKey }) } return Object.assign( @@ -188,14 +205,14 @@ export const reloadOnError = ({ return { ...options, - onSuccess: ({ load }) => { - options.onSuccess?.({ load }) - reloadStorage.removeItem(load.toString()) - }, - onError: ({ error, load }) => { - options.onError?.({ error, load }) - - const storageKey = load.toString() + onSuccess: ((arg: any) => { + ;(options.onSuccess as any)?.(arg) + const storageKey = arg?.[reloadOnErrorStorageKeySymbol] as string + reloadStorage.removeItem(storageKey) + }) as LazyOptions['onSuccess'], + onError: (errorOptions: { error: unknown }) => { + options.onError?.(errorOptions) + const storageKey = (errorOptions as any)[reloadOnErrorStorageKeySymbol] as string let currentRetryCount = 0 if (typeof retry === 'number') {