|
| 1 | +--- |
| 2 | +name: SSR Hooks |
| 3 | +menu: Usage |
| 4 | +route: '/usage/ssr-hooks' |
| 5 | +--- |
| 6 | + |
| 7 | +# Server Side Renderering |
| 8 | + |
| 9 | +## Setup |
| 10 | + |
| 11 | +To SSR your hook, you must ensure `react-dom >= 16.9.0` is installed in your project and then import |
| 12 | +the server module in your test: |
| 13 | + |
| 14 | +```ts |
| 15 | +import { renderHook } from '@testing-library/react-hooks/server' |
| 16 | +``` |
| 17 | + |
| 18 | +## Render Hook |
| 19 | + |
| 20 | +`renderHook` when called returns the same result as documented in the |
| 21 | +[API](/reference/api#renderhook-result) but includes an additional argument, `hydrate`: |
| 22 | + |
| 23 | +```ts |
| 24 | +function hydrate(): void |
| 25 | +``` |
| 26 | + |
| 27 | +The `hydrate` function is a light wrapper around |
| 28 | +[`ReactDOM.hydrate`](https://reactjs.org/docs/react-dom.html#hydrate) but no arguments are required |
| 29 | +as the library will pass the element & container for you. Remember, certain effects such as |
| 30 | +`useEffect` will not run server side and `hydrate` must be called before those effects are |
| 31 | +ran.`hydrate`is also necessary before the first `act` or `rerender` call. For more information on |
| 32 | +`hydrate` see the [API documentation](/reference/api#hydrate). There is also an |
| 33 | +[example below](/usage/ssr-hooks#example) |
| 34 | + |
| 35 | +## Example |
| 36 | + |
| 37 | +### Hydration |
| 38 | + |
| 39 | +```js |
| 40 | +import { renderHook, act } from '@testing-library/react-hooks/server' |
| 41 | +
|
| 42 | +describe('custom hook tests', () => { |
| 43 | + function useCounter() { |
| 44 | + const [count, setCount] = useState(0) |
| 45 | +
|
| 46 | + const increment = useCallback(() => setCount(count + 1), [count]) |
| 47 | + const decrement = useCallback(() => setCount(count - 1), [count]) |
| 48 | +
|
| 49 | + return { count, increment, decrement } |
| 50 | + } |
| 51 | +
|
| 52 | + test('should decrement counter', () => { |
| 53 | + const { result, hydrate } = renderHook(() => useCounter()) |
| 54 | +
|
| 55 | + expect(result.current.count).toBe(0) |
| 56 | +
|
| 57 | + // hydrate is called because we want to interact with the hook |
| 58 | + hydrate() |
| 59 | +
|
| 60 | + act(() => result.current.decrement()) |
| 61 | +
|
| 62 | + expect(result.current.count).toBe(-1) |
| 63 | + }) |
| 64 | +}) |
| 65 | +``` |
| 66 | + |
| 67 | +### Effects |
| 68 | + |
| 69 | +```js |
| 70 | +describe('useEffect tests', () => { |
| 71 | + test('should handle useEffect hook', () => { |
| 72 | + const sideEffect = { 1: false, 2: false } |
| 73 | +
|
| 74 | + const useEffectHook = ({ id }) => { |
| 75 | + useEffect(() => { |
| 76 | + sideEffect[id] = true |
| 77 | + return () => { |
| 78 | + sideEffect[id] = false |
| 79 | + } |
| 80 | + }, [id]) |
| 81 | + } |
| 82 | +
|
| 83 | + const { hydrate, rerender, unmount } = renderHook((id) => useEffectHook({ id }), { |
| 84 | + initialProps: { id: 1 } |
| 85 | + }) |
| 86 | +
|
| 87 | + expect(sideEffect[1]).toBe(false) |
| 88 | + expect(sideEffect[2]).toBe(false) |
| 89 | +
|
| 90 | + hydrate() |
| 91 | +
|
| 92 | + expect(sideEffect[1]).toBe(true) |
| 93 | + expect(sideEffect[2]).toBe(false) |
| 94 | + }) |
| 95 | +}) |
| 96 | +``` |
0 commit comments