diff --git a/src/__tests__/renderHook.js b/src/__tests__/renderHook.js
index f331e90e..d4d425e0 100644
--- a/src/__tests__/renderHook.js
+++ b/src/__tests__/renderHook.js
@@ -50,6 +50,26 @@ test('allows rerendering', () => {
expect(result.current).toEqual(['right', expect.any(Function)])
})
+test('allows rerendering with multiple arguments', () => {
+ const useTest = (arg1, arg2, arg3) => arg1 + arg2 + arg3
+ const {result, rerender} = renderHook(useTest, {initialArgs: [2, 3, 4]})
+ expect(result.current).toBe(9)
+ rerender(3, 4, -1)
+ expect(result.current).toBe(6)
+})
+
+test('throws on invalid options', () => {
+ const useTest = (arg1, arg2) => arg1 + arg2
+ expect(() => {
+ renderHook(useTest, {initialProps: {}, initialArgs: []})
+ }).toThrow(
+ 'Options `initialProps` and `initialArgs` cannot be used together.',
+ )
+ expect(() => {
+ renderHook(useTest, {initialArgs: {}})
+ }).toThrow('Option `initialArgs` must be an array.')
+})
+
test('allows wrapper components', async () => {
const Context = React.createContext('default')
function Wrapper({children}) {
diff --git a/src/pure.js b/src/pure.js
index 0f9c487d..7ca6a45f 100644
--- a/src/pure.js
+++ b/src/pure.js
@@ -318,7 +318,7 @@ function cleanup() {
}
function renderHook(renderCallback, options = {}) {
- const {initialProps, ...renderOptions} = options
+ const {initialProps, initialArgs, ...renderOptions} = options
if (renderOptions.legacyRoot && typeof ReactDOM.render !== 'function') {
const error = new Error(
@@ -330,10 +330,23 @@ function renderHook(renderCallback, options = {}) {
throw error
}
+ if (initialProps && initialArgs) {
+ throw new Error(
+ 'Options `initialProps` and `initialArgs` cannot be used together.',
+ )
+ }
+
+ if (initialArgs !== undefined && !Array.isArray(initialArgs)) {
+ throw new Error('Option `initialArgs` must be an array.')
+ }
+
+ // convert `initialProps` to an empty or single-element array
+ const initial = initialArgs || (initialProps ? [initialProps] : [])
+
const result = React.createRef()
- function TestComponent({renderCallbackProps}) {
- const pendingResult = renderCallback(renderCallbackProps)
+ function TestComponent({renderCallbackArgs}) {
+ const pendingResult = renderCallback(...renderCallbackArgs)
React.useEffect(() => {
result.current = pendingResult
@@ -343,13 +356,13 @@ function renderHook(renderCallback, options = {}) {
}
const {rerender: baseRerender, unmount} = render(
- ,
+ ,
renderOptions,
)
- function rerender(rerenderCallbackProps) {
+ function rerender(...rerenderCallbackArgs) {
return baseRerender(
- ,
+ ,
)
}
diff --git a/types/index.d.ts b/types/index.d.ts
index bdd60567..9abedc77 100644
--- a/types/index.d.ts
+++ b/types/index.d.ts
@@ -181,11 +181,12 @@ export function render(
options?: Omit | undefined,
): RenderResult
-export interface RenderHookResult {
+export interface RenderHookArgsResult {
/**
- * Triggers a re-render. The props will be passed to your renderHook callback.
+ * Triggers a re-render. The arguments will be passed to your renderHook
+ * callback.
*/
- rerender: (props?: Props) => void
+ rerender: (...args: Args) => void
/**
* This is a stable reference to the latest value returned by your renderHook
* callback
@@ -203,6 +204,11 @@ export interface RenderHookResult {
unmount: () => void
}
+export type RenderHookResult = RenderHookArgsResult<
+ Result,
+ [Props?]
+>
+
/** @deprecated */
export type BaseRenderHookOptions<
Props,
@@ -256,6 +262,19 @@ export interface RenderHookOptions<
initialProps?: Props | undefined
}
+export interface RenderHookArgsOptions<
+ Args extends any[],
+ Q extends Queries = typeof queries,
+ Container extends RendererableContainer | HydrateableContainer = HTMLElement,
+ BaseElement extends RendererableContainer | HydrateableContainer = Container,
+> extends RenderOptions {
+ /**
+ * The argument passed to the renderHook callback. Can be useful if you plan
+ * to use the rerender utility to change the values passed to your hook.
+ */
+ initialArgs: Args
+}
+
/**
* Allows you to render a hook within a test React component without having to
* create that component yourself.
@@ -271,6 +290,21 @@ export function renderHook<
options?: RenderHookOptions | undefined,
): RenderHookResult
+/**
+ * Allows you to render a hook within a test React component without having to
+ * create that component yourself.
+ */
+export function renderHook<
+ Result,
+ Args extends any[],
+ Q extends Queries = typeof queries,
+ Container extends RendererableContainer | HydrateableContainer = HTMLElement,
+ BaseElement extends RendererableContainer | HydrateableContainer = Container,
+>(
+ render: (...initialArgs: Args) => Result,
+ options: RenderHookArgsOptions | undefined,
+): RenderHookArgsResult
+
/**
* Unmounts React trees that were mounted with render.
*/
diff --git a/types/test.tsx b/types/test.tsx
index 825d5699..87195002 100644
--- a/types/test.tsx
+++ b/types/test.tsx
@@ -237,6 +237,16 @@ export function testRenderHookProps() {
unmount()
}
+export function testRenderHookArgs() {
+ const useTest = (s: string, n: number): string => s.repeat(n)
+ const {result, rerender, unmount} = renderHook(useTest, {
+ initialArgs: ['a', 2],
+ })
+ expectType(result.current)
+ rerender('b', 3)
+ unmount()
+}
+
export function testContainer() {
render('a', {container: document.createElement('div')})
render('a', {container: document.createDocumentFragment()})