diff --git a/.changeset/honest-peaches-deny.md b/.changeset/honest-peaches-deny.md new file mode 100644 index 000000000000..5bffda8fce93 --- /dev/null +++ b/.changeset/honest-peaches-deny.md @@ -0,0 +1,9 @@ +--- +"@refinedev/core": patch +--- + +fix(core): `useResourceParams` not reflecting `id` prop changes immediately + +`useResourceParams` hook was not reflecting the changes in the `id` prop immediately. This was due to the `id` state being set in the `useEffect` hook. This PR fixes the issue by setting the `id` state properly during render rather than after the render is complete. + +[Fixes #6259](https://github.com/refinedev/refine/issues/6259) diff --git a/packages/core/src/hooks/use-resource-params/index.spec.ts b/packages/core/src/hooks/use-resource-params/index.spec.ts index 38115398558f..6b7ef4bd8eea 100644 --- a/packages/core/src/hooks/use-resource-params/index.spec.ts +++ b/packages/core/src/hooks/use-resource-params/index.spec.ts @@ -8,6 +8,8 @@ import { import { useResourceParams } from "."; +import type { BaseKey } from "../../contexts/data/types"; + describe("useResourceParams Hook", () => { describe("with routerProvider", () => { it("returns undefined when routerProvider doesn't have params", () => { @@ -405,6 +407,152 @@ describe("useResourceParams Hook", () => { ); }); }); + + it("should reflect changes in id prop immediately", async () => { + const { result, rerender } = renderHook( + ({ id }) => useResourceParams({ id }), + { + wrapper: TestWrapper({}), + initialProps: { + id: undefined, + } as { id?: BaseKey }, + }, + ); + + expect(result.current).toMatchObject( + expect.objectContaining({ + resource: undefined, + id: undefined, + action: undefined, + formAction: "create", + identifier: undefined, + }), + ); + + rerender({ id: "123" }); + + expect(result.current).toMatchObject( + expect.objectContaining({ + resource: undefined, + id: "123", + action: undefined, + formAction: "create", + identifier: undefined, + }), + ); + }); + + it("should reflect both id prop changes and setId invocations", async () => { + const { result, rerender } = renderHook( + ({ id }) => useResourceParams({ id }), + { + wrapper: TestWrapper({}), + initialProps: { + id: undefined, + } as { id?: BaseKey }, + }, + ); + + expect(result.current).toMatchObject( + expect.objectContaining({ + resource: undefined, + id: undefined, + action: undefined, + formAction: "create", + identifier: undefined, + }), + ); + + act(() => { + result.current.setId("123"); + }); + + expect(result.current).toMatchObject( + expect.objectContaining({ + resource: undefined, + id: "123", + action: undefined, + formAction: "create", + identifier: undefined, + }), + ); + + rerender({ id: "456" }); + + expect(result.current).toMatchObject( + expect.objectContaining({ + resource: undefined, + id: "456", + action: undefined, + formAction: "create", + identifier: undefined, + }), + ); + }); + + it("should respect value set by setId method", async () => { + const { result, rerender } = renderHook( + ({ id }) => useResourceParams({ id }), + { + wrapper: TestWrapper({}), + initialProps: { + id: "123", + } as { id?: BaseKey }, + }, + ); + + expect(result.current).toMatchObject( + expect.objectContaining({ + id: "123", + }), + ); + + act(() => { + result.current.setId(undefined); + }); + + expect(result.current).toMatchObject( + expect.objectContaining({ + id: undefined, + }), + ); + + rerender({ id: "456" }); + + expect(result.current).toMatchObject( + expect.objectContaining({ + id: "456", + }), + ); + }); + + it("should provide id prop in setId setter", async () => { + const { result, rerender } = renderHook( + ({ id }) => useResourceParams({ id }), + { + wrapper: TestWrapper({}), + initialProps: { + id: "123", + } as { id?: BaseKey }, + }, + ); + + expect(result.current).toMatchObject( + expect.objectContaining({ + id: "123", + }), + ); + + act(() => { + result.current.setId((prev) => (Number(prev) + 1).toString()); + }); + + expect(result.current).toMatchObject( + expect.objectContaining({ + id: "124", + }), + ); + }); }); describe("with legacyRouterProvider", () => { diff --git a/packages/core/src/hooks/use-resource-params/index.ts b/packages/core/src/hooks/use-resource-params/index.ts index c2b959d522e5..3673e0e18330 100644 --- a/packages/core/src/hooks/use-resource-params/index.ts +++ b/packages/core/src/hooks/use-resource-params/index.ts @@ -64,7 +64,7 @@ export function useResourceParams(props?: Props): ResourceParams { const [id, setId] = React.useState(defaultId); - React.useEffect(() => setId(defaultId), [defaultId]); + React.useMemo(() => setId(defaultId), [defaultId]); const formAction = React.useMemo(() => { if (!isSameResource && !props?.action) {