Skip to content

Commit b5f7249

Browse files
committed
feat: add support for multiple arguments in renderHook()
1 parent 9fc6a75 commit b5f7249

File tree

4 files changed

+86
-9
lines changed

4 files changed

+86
-9
lines changed

src/__tests__/renderHook.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,26 @@ test('allows rerendering', () => {
5050
expect(result.current).toEqual(['right', expect.any(Function)])
5151
})
5252

53+
test('allows rerendering with multiple arguments', () => {
54+
const useTest = (arg1, arg2, arg3) => arg1 + arg2 + arg3
55+
const {result, rerender} = renderHook(useTest, {initialArgs: [2, 3, 4]})
56+
expect(result.current).toBe(9)
57+
rerender(3, 4, -1)
58+
expect(result.current).toBe(6)
59+
})
60+
61+
test('throws on invalid options', () => {
62+
const useTest = (arg1, arg2) => arg1 + arg2
63+
expect(() => {
64+
renderHook(useTest, {initialProps: {}, initialArgs: []})
65+
}).toThrow(
66+
'Options `initialProps` and `initialArgs` cannot be used together.',
67+
)
68+
expect(() => {
69+
renderHook(useTest, {initialArgs: {}})
70+
}).toThrow('Option `initialArgs` must be an array.')
71+
})
72+
5373
test('allows wrapper components', async () => {
5474
const Context = React.createContext('default')
5575
function Wrapper({children}) {

src/pure.js

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ function cleanup() {
318318
}
319319

320320
function renderHook(renderCallback, options = {}) {
321-
const {initialProps, ...renderOptions} = options
321+
const {initialProps, initialArgs, ...renderOptions} = options
322322

323323
if (renderOptions.legacyRoot && typeof ReactDOM.render !== 'function') {
324324
const error = new Error(
@@ -330,10 +330,23 @@ function renderHook(renderCallback, options = {}) {
330330
throw error
331331
}
332332

333+
if (initialProps && initialArgs) {
334+
throw new Error(
335+
'Options `initialProps` and `initialArgs` cannot be used together.',
336+
)
337+
}
338+
339+
if (initialArgs !== undefined && !Array.isArray(initialArgs)) {
340+
throw new Error('Option `initialArgs` must be an array.')
341+
}
342+
343+
// convert `initialProps` to an empty or single-element array
344+
const initial = initialArgs || (initialProps ? [initialProps] : [])
345+
333346
const result = React.createRef()
334347

335-
function TestComponent({renderCallbackProps}) {
336-
const pendingResult = renderCallback(renderCallbackProps)
348+
function TestComponent({renderCallbackArgs}) {
349+
const pendingResult = renderCallback(...renderCallbackArgs)
337350

338351
React.useEffect(() => {
339352
result.current = pendingResult
@@ -343,13 +356,13 @@ function renderHook(renderCallback, options = {}) {
343356
}
344357

345358
const {rerender: baseRerender, unmount} = render(
346-
<TestComponent renderCallbackProps={initialProps} />,
359+
<TestComponent renderCallbackArgs={initial} />,
347360
renderOptions,
348361
)
349362

350-
function rerender(rerenderCallbackProps) {
363+
function rerender(...rerenderCallbackArgs) {
351364
return baseRerender(
352-
<TestComponent renderCallbackProps={rerenderCallbackProps} />,
365+
<TestComponent renderCallbackArgs={rerenderCallbackArgs} />,
353366
)
354367
}
355368

types/index.d.ts

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -181,11 +181,12 @@ export function render(
181181
options?: Omit<RenderOptions, 'queries'> | undefined,
182182
): RenderResult
183183

184-
export interface RenderHookResult<Result, Props> {
184+
export interface RenderHookArgsResult<Result, Args extends any[]> {
185185
/**
186-
* Triggers a re-render. The props will be passed to your renderHook callback.
186+
* Triggers a re-render. The arguments will be passed to your renderHook
187+
* callback.
187188
*/
188-
rerender: (props?: Props) => void
189+
rerender: (...args: Args) => void
189190
/**
190191
* This is a stable reference to the latest value returned by your renderHook
191192
* callback
@@ -203,6 +204,11 @@ export interface RenderHookResult<Result, Props> {
203204
unmount: () => void
204205
}
205206

207+
export type RenderHookResult<Result, Props> = RenderHookArgsResult<
208+
Result,
209+
[Props?]
210+
>
211+
206212
/** @deprecated */
207213
export type BaseRenderHookOptions<
208214
Props,
@@ -256,6 +262,19 @@ export interface RenderHookOptions<
256262
initialProps?: Props | undefined
257263
}
258264

265+
export interface RenderHookArgsOptions<
266+
Args extends any[],
267+
Q extends Queries = typeof queries,
268+
Container extends RendererableContainer | HydrateableContainer = HTMLElement,
269+
BaseElement extends RendererableContainer | HydrateableContainer = Container,
270+
> extends RenderOptions<Q, Container, BaseElement> {
271+
/**
272+
* The argument passed to the renderHook callback. Can be useful if you plan
273+
* to use the rerender utility to change the values passed to your hook.
274+
*/
275+
initialArgs: Args
276+
}
277+
259278
/**
260279
* Allows you to render a hook within a test React component without having to
261280
* create that component yourself.
@@ -271,6 +290,21 @@ export function renderHook<
271290
options?: RenderHookOptions<Props, Q, Container, BaseElement> | undefined,
272291
): RenderHookResult<Result, Props>
273292

293+
/**
294+
* Allows you to render a hook within a test React component without having to
295+
* create that component yourself.
296+
*/
297+
export function renderHook<
298+
Result,
299+
Args extends any[],
300+
Q extends Queries = typeof queries,
301+
Container extends RendererableContainer | HydrateableContainer = HTMLElement,
302+
BaseElement extends RendererableContainer | HydrateableContainer = Container,
303+
>(
304+
render: (...initialArgs: Args) => Result,
305+
options: RenderHookArgsOptions<Args, Q, Container, BaseElement> | undefined,
306+
): RenderHookArgsResult<Result, Args>
307+
274308
/**
275309
* Unmounts React trees that were mounted with render.
276310
*/

types/test.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,16 @@ export function testRenderHookProps() {
237237
unmount()
238238
}
239239

240+
export function testRenderHookArgs() {
241+
const useTest = (s: string, n: number): string => s.repeat(n)
242+
const {result, rerender, unmount} = renderHook(useTest, {
243+
initialArgs: ['a', 2],
244+
})
245+
expectType<string, typeof result.current>(result.current)
246+
rerender('b', 3)
247+
unmount()
248+
}
249+
240250
export function testContainer() {
241251
render('a', {container: document.createElement('div')})
242252
render('a', {container: document.createDocumentFragment()})

0 commit comments

Comments
 (0)