From 560b7ca4c729028749d2e9ac48b105d516a09278 Mon Sep 17 00:00:00 2001 From: Maciej Jastrzebski Date: Tue, 14 Jan 2025 12:13:37 +0100 Subject: [PATCH 1/3] . --- src/render-hook.tsx | 50 +++++++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/src/render-hook.tsx b/src/render-hook.tsx index 98f268a0..b7af3b31 100644 --- a/src/render-hook.tsx +++ b/src/render-hook.tsx @@ -1,30 +1,42 @@ -import type { ComponentType } from 'react'; -import React from 'react'; +import * as React from 'react'; import { renderInternal } from './render'; export type RenderHookResult = { rerender: (props: Props) => void; - result: { current: Result }; + result: React.MutableRefObject; unmount: () => void; }; export type RenderHookOptions = { + /** + * The initial props to pass to the hook. + */ initialProps?: Props; + + /** + * Pass a React Component as the wrapper option to have it rendered around the inner element. This is most useful for creating + * reusable custom render functions for common data providers. + */ // eslint-disable-next-line @typescript-eslint/no-explicit-any - wrapper?: ComponentType; + wrapper?: React.ComponentType; + + /** + * Set to `false` to disable concurrent rendering. + * Otherwise `renderHook` will default to concurrent rendering. + */ + concurrentRoot?: boolean; }; export function renderHook( - renderCallback: (props: Props) => Result, + hookToRender: (props: Props) => Result, options?: RenderHookOptions, ): RenderHookResult { - const initialProps = options?.initialProps; - const wrapper = options?.wrapper; + const { initialProps, ...renderOptions } = options ?? {}; const result: React.MutableRefObject = React.createRef(); - function TestComponent({ renderCallbackProps }: { renderCallbackProps: Props }) { - const renderResult = renderCallback(renderCallbackProps); + function TestComponent({ hookProps: hookProps }: { hookProps: Props }) { + const renderResult = hookToRender(hookProps); React.useEffect(() => { result.current = renderResult; @@ -33,18 +45,20 @@ export function renderHook( return null; } - const { rerender: baseRerender, unmount } = renderInternal( + const { rerender: componentRerender, unmount } = renderInternal( // @ts-expect-error since option can be undefined, initialProps can be undefined when it should'nt - , - { - wrapper, - }, + , + renderOptions, ); - function rerender(rerenderCallbackProps: Props) { - return baseRerender(); + function rerender(hookProps: Props) { + return componentRerender(); } - // @ts-expect-error result is ill typed because ref is initialized to null - return { result, rerender, unmount }; + return { + // Result should already be set after the first render effects are run. + result: result as React.MutableRefObject, + rerender, + unmount, + }; } From 7af00ed791e98f2802eab5561222eb6dae9f44ab Mon Sep 17 00:00:00 2001 From: Maciej Jastrzebski Date: Tue, 14 Jan 2025 12:17:36 +0100 Subject: [PATCH 2/3] . --- website/docs/13.x-next/docs/api/misc/render-hook.mdx | 9 +++++++-- website/docs/13.x-next/docs/api/render.mdx | 12 ++++++------ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/website/docs/13.x-next/docs/api/misc/render-hook.mdx b/website/docs/13.x-next/docs/api/misc/render-hook.mdx index 9fe20b54..5767aaaf 100644 --- a/website/docs/13.x-next/docs/api/misc/render-hook.mdx +++ b/website/docs/13.x-next/docs/api/misc/render-hook.mdx @@ -45,7 +45,7 @@ The `props` passed into the callback will be the `initialProps` provided in the A `RenderHookOptions` object to modify the execution of the `callback` function, containing the following properties: -### `initialProps` +### `initialProps` {#initial-props} The initial values to pass as `props` to the `callback` function of `renderHook`. The `Props` type is determined by the type passed to or inferred by the `renderHook` call. @@ -53,7 +53,12 @@ The initial values to pass as `props` to the `callback` function of `renderHook` A React component to wrap the test component in when rendering. This is usually used to add context providers from `React.createContext` for the hook to access with `useContext`. -## `RenderHookResult` +### `concurrentRoot` {#concurrent-root} + +Set to `false` to disable concurrent rendering. +Otherwise, `render` will default to using concurrent rendering used in the React Native New Architecture. + +## Result ```ts interface RenderHookResult { diff --git a/website/docs/13.x-next/docs/api/render.mdx b/website/docs/13.x-next/docs/api/render.mdx index f97ed2e1..5022b87b 100644 --- a/website/docs/13.x-next/docs/api/render.mdx +++ b/website/docs/13.x-next/docs/api/render.mdx @@ -20,11 +20,11 @@ test('basic test', () => { > When using React context providers, like Redux Provider, you'll likely want to wrap rendered component with them. In such cases, it's convenient to create your own custom `render` method. [Follow this great guide on how to set this up](https://testing-library.com/docs/react-testing-library/setup#custom-render). -### Options {#render-options} +## Options The behavior of the `render` method can be customized by passing various options as a second argument of the `RenderOptions` type: -#### `wrapper` option +### `wrapper` option ```ts wrapper?: React.ComponentType, @@ -32,12 +32,12 @@ wrapper?: React.ComponentType, This option allows you to wrap the tested component, passed as the first option to the `render()` function, in an additional wrapper component. This is useful for creating reusable custom render functions for common React Context providers. -#### `concurrentRoot` option {#concurrent-root} +### `concurrentRoot` option {#concurrent-root} Set to `false` to disable concurrent rendering. Otherwise, `render` will default to using concurrent rendering used in the React Native New Architecture. -#### `createNodeMock` option +### `createNodeMock` option ```ts createNodeMock?: (element: React.Element) => unknown, @@ -45,7 +45,7 @@ createNodeMock?: (element: React.Element) => unknown, This option allows you to pass `createNodeMock` option to `ReactTestRenderer.create()` method in order to allow for custom mock refs. You can learn more about this option from [React Test Renderer documentation](https://reactjs.org/docs/test-renderer.html#ideas). -#### `unstable_validateStringsRenderedWithinText` option +### `unstable_validateStringsRenderedWithinText` option ```ts unstable_validateStringsRenderedWithinText?: boolean; @@ -59,7 +59,7 @@ This **experimental** option allows you to replicate React Native behavior of th React Test Renderer does not enforce this check; hence, by default, React Native Testing Library also does not check this. That might result in runtime errors when running your code on a device, while the code works without errors in tests. -### Result {#render-result} +## Result The `render` function returns the same queries and utilities as the [`screen`](docs/api/screen) object. We recommended using the `screen` object as more developer-friendly way. From 62e8b52ab988d65d6abade44eb68c094631d9a94 Mon Sep 17 00:00:00 2001 From: Maciej Jastrzebski Date: Tue, 14 Jan 2025 12:23:53 +0100 Subject: [PATCH 3/3] . --- src/render-hook.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/render-hook.tsx b/src/render-hook.tsx index b7af3b31..6e8b3d5e 100644 --- a/src/render-hook.tsx +++ b/src/render-hook.tsx @@ -35,7 +35,7 @@ export function renderHook( const result: React.MutableRefObject = React.createRef(); - function TestComponent({ hookProps: hookProps }: { hookProps: Props }) { + function TestComponent({ hookProps }: { hookProps: Props }) { const renderResult = hookToRender(hookProps); React.useEffect(() => {