From e155799685189938e7886fc394904a038ad58069 Mon Sep 17 00:00:00 2001 From: imevanc Date: Wed, 12 Mar 2025 22:46:03 +0000 Subject: [PATCH 1/3] feat: add render hook support for multiple arguments --- src/__tests__/renderHook.js | 25 +++++++++++++++++++++++++ src/pure.js | 27 ++++++++++++++++++++++----- types/index.d.ts | 3 ++- 3 files changed, 49 insertions(+), 6 deletions(-) diff --git a/src/__tests__/renderHook.js b/src/__tests__/renderHook.js index fe7551a2..5988c1e7 100644 --- a/src/__tests__/renderHook.js +++ b/src/__tests__/renderHook.js @@ -111,3 +111,28 @@ testGateReact19('legacyRoot throws', () => { `\`legacyRoot: true\` is not supported in this version of React. If your app runs React 19 or later, you should remove this flag. If your app runs React 18 or earlier, visit https://react.dev/blog/2022/03/08/react-18-upgrade-guide for upgrade instructions.`, ) }) + +test('supports initialArgs for multi-parameter hooks', () => { + const useTestHook = (a, b, c) => a + b + c + const {result} = renderHook(useTestHook, {initialArgs: [1, 2, 3]}) + + expect(result.current).toBe(6) +}) + +test('rerender supports multiple parameters', () => { + const useTestHook = (a, b) => a * b + const {result, rerender} = renderHook(useTestHook, {initialArgs: [2, 3]}) + + expect(result.current).toBe(6) + + rerender(4, 5) + expect(result.current).toBe(20) +}) + +test('throws error when both initialProps and initialArgs are used', () => { + const useTestHook = (a, b) => a + b + + expect(() => + renderHook(useTestHook, {initialProps: 1, initialArgs: [2, 3]}), + ).toThrow('Cannot use both initialProps and initialArgs. Choose one.') +}) diff --git a/src/pure.js b/src/pure.js index fe95024a..2975a6bc 100644 --- a/src/pure.js +++ b/src/pure.js @@ -290,7 +290,11 @@ function cleanup() { } function renderHook(renderCallback, options = {}) { - const {initialProps, ...renderOptions} = options + const {initialProps, initialArgs, ...renderOptions} = options + + if (initialProps !== undefined && initialArgs !== undefined) { + throw new Error('Cannot use both initialProps and initialArgs. Choose one.') + } if (renderOptions.legacyRoot && typeof ReactDOM.render !== 'function') { const error = new Error( @@ -303,9 +307,18 @@ function renderHook(renderCallback, options = {}) { } const result = React.createRef() + const isUsingArgs = initialArgs !== undefined function TestComponent({renderCallbackProps}) { - const pendingResult = renderCallback(renderCallbackProps) + // Handle both cases: when renderCallbackProps is an array to spread, + // and when it's a single value not to spread + const pendingResult = isUsingArgs + ? renderCallback( + ...(Array.isArray(renderCallbackProps) + ? renderCallbackProps + : [renderCallbackProps]), + ) + : renderCallback(renderCallbackProps) React.useEffect(() => { result.current = pendingResult @@ -315,13 +328,17 @@ function renderHook(renderCallback, options = {}) { } const {rerender: baseRerender, unmount} = render( - , + , renderOptions, ) - function rerender(rerenderCallbackProps) { + function rerender(...newArgs) { return baseRerender( - , + , ) } diff --git a/types/index.d.ts b/types/index.d.ts index 2f814a6d..47f2a24c 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -249,6 +249,7 @@ export interface RenderHookOptions< * to use the rerender utility to change the values passed to your hook. */ initialProps?: Props | undefined + initialArgs?: Props | undefined } /** @@ -262,7 +263,7 @@ export function renderHook< Container extends RendererableContainer | HydrateableContainer = HTMLElement, BaseElement extends RendererableContainer | HydrateableContainer = Container, >( - render: (initialProps: Props) => Result, + render: (initialProps: Props, initialArgs: Props) => Result, options?: RenderHookOptions | undefined, ): RenderHookResult From 0fb349c9cd3bc6fa1e035fd0b871ed140d781cb7 Mon Sep 17 00:00:00 2001 From: imevanc Date: Wed, 12 Mar 2025 23:01:15 +0000 Subject: [PATCH 2/3] feat: increase coverage to fix ci --- src/__tests__/renderHook.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/__tests__/renderHook.js b/src/__tests__/renderHook.js index 5988c1e7..663f1e15 100644 --- a/src/__tests__/renderHook.js +++ b/src/__tests__/renderHook.js @@ -1,4 +1,6 @@ import React from 'react' +import ReactDOM from 'react-dom' + import {renderHook} from '../pure' const isReact18 = React.version.startsWith('18.') @@ -136,3 +138,24 @@ test('throws error when both initialProps and initialArgs are used', () => { renderHook(useTestHook, {initialProps: 1, initialArgs: [2, 3]}), ).toThrow('Cannot use both initialProps and initialArgs. Choose one.') }) + +test('throws an error when legacyRoot is used in unsupported React versions', () => { + const useTestHook = () => 'test' + + // Save the original ReactDOM.render + const {render: originalRender} = ReactDOM + + // Mock by direct assignment + ReactDOM.render = undefined + + expect(() => { + renderHook(useTestHook, {legacyRoot: true}) + }).toThrowError( + '`legacyRoot: true` is not supported in this version of React. ' + + 'If your app runs React 19 or later, you should remove this flag. ' + + 'If your app runs React 18 or earlier, visit https://react.dev/blog/2022/03/08/react-18-upgrade-guide for upgrade instructions.', + ) + + // Restore the original + ReactDOM.render = originalRender +}) From 18196db2db7cfdabd50f84e74199f07d4bf32e32 Mon Sep 17 00:00:00 2001 From: imevanc Date: Wed, 12 Mar 2025 23:06:59 +0000 Subject: [PATCH 3/3] feat: housekeeping --- src/__tests__/renderHook.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/__tests__/renderHook.js b/src/__tests__/renderHook.js index 663f1e15..53ff6f1f 100644 --- a/src/__tests__/renderHook.js +++ b/src/__tests__/renderHook.js @@ -142,10 +142,8 @@ test('throws error when both initialProps and initialArgs are used', () => { test('throws an error when legacyRoot is used in unsupported React versions', () => { const useTestHook = () => 'test' - // Save the original ReactDOM.render const {render: originalRender} = ReactDOM - // Mock by direct assignment ReactDOM.render = undefined expect(() => { @@ -156,6 +154,5 @@ test('throws an error when legacyRoot is used in unsupported React versions', () 'If your app runs React 18 or earlier, visit https://react.dev/blog/2022/03/08/react-18-upgrade-guide for upgrade instructions.', ) - // Restore the original ReactDOM.render = originalRender })