Skip to content

Commit d8c6b4d

Browse files
authored
fix(render): Actually hydrate with given ui (#988)
1 parent 68d2a23 commit d8c6b4d

File tree

3 files changed

+57
-12
lines changed

3 files changed

+57
-12
lines changed

jest.config.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ module.exports = Object.assign(jestConfig, {
66
// full coverage across the build matrix (React 17, 18) but not in a single job
77
'./src/pure': {
88
// minimum coverage of jobs using React 17 and 18
9-
branches: 80,
9+
branches: 75,
1010
functions: 78,
11-
lines: 79,
12-
statements: 79,
11+
lines: 76,
12+
statements: 76,
1313
},
1414
},
1515
})

src/__tests__/render.js

+35-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
import * as React from 'react'
22
import ReactDOM from 'react-dom'
3-
import {render, screen} from '../'
3+
import ReactDOMServer from 'react-dom/server'
4+
import {fireEvent, render, screen} from '../'
5+
6+
afterEach(() => {
7+
if (console.error.mockRestore !== undefined) {
8+
console.error.mockRestore()
9+
}
10+
})
411

512
test('renders div into document', () => {
613
const ref = React.createRef()
@@ -134,3 +141,30 @@ test('can be called multiple times on the same container', () => {
134141

135142
expect(container).toBeEmptyDOMElement()
136143
})
144+
145+
test('hydrate will make the UI interactive', () => {
146+
jest.spyOn(console, 'error').mockImplementation(() => {})
147+
function App() {
148+
const [clicked, handleClick] = React.useReducer(n => n + 1, 0)
149+
150+
return (
151+
<button type="button" onClick={handleClick}>
152+
clicked:{clicked}
153+
</button>
154+
)
155+
}
156+
const ui = <App />
157+
const container = document.createElement('div')
158+
document.body.appendChild(container)
159+
container.innerHTML = ReactDOMServer.renderToString(ui)
160+
161+
expect(container).toHaveTextContent('clicked:0')
162+
163+
render(ui, {container, hydrate: true})
164+
165+
expect(console.error).not.toHaveBeenCalled()
166+
167+
fireEvent.click(container.querySelector('button'))
168+
169+
expect(container).toHaveTextContent('clicked:1')
170+
})

src/pure.js

+19-8
Original file line numberDiff line numberDiff line change
@@ -60,25 +60,36 @@ const mountedContainers = new Set()
6060
*/
6161
const mountedRootEntries = []
6262

63-
function createConcurrentRoot(container, options) {
63+
function createConcurrentRoot(
64+
container,
65+
{hydrate, ui, wrapper: WrapperComponent},
66+
) {
6467
if (typeof ReactDOM.createRoot !== 'function') {
6568
throw new TypeError(
6669
`Attempted to use concurrent React with \`react-dom@${ReactDOM.version}\`. Be sure to use the \`next\` or \`experimental\` release channel (https://reactjs.org/docs/release-channels.html).'`,
6770
)
6871
}
69-
const root = options.hydrate
70-
? ReactDOM.hydrateRoot(container)
71-
: ReactDOM.createRoot(container)
72+
let root
73+
if (hydrate) {
74+
act(() => {
75+
root = ReactDOM.hydrateRoot(
76+
container,
77+
WrapperComponent ? React.createElement(WrapperComponent, null, ui) : ui,
78+
)
79+
})
80+
} else {
81+
root = ReactDOM.createRoot(container)
82+
}
7283

7384
return {
74-
hydrate(element) {
85+
hydrate() {
7586
/* istanbul ignore if */
76-
if (!options.hydrate) {
87+
if (!hydrate) {
7788
throw new Error(
7889
'Attempted to hydrate a non-hydrateable root. This is a bug in `@testing-library/react`.',
7990
)
8091
}
81-
root.render(element)
92+
// Nothing to do since hydration happens when creating the root object.
8293
},
8394
render(element) {
8495
root.render(element)
@@ -183,7 +194,7 @@ function render(
183194
// eslint-disable-next-line no-negated-condition -- we want to map the evolution of this over time. The root is created first. Only later is it re-used so we don't want to read the case that happens later first.
184195
if (!mountedContainers.has(container)) {
185196
const createRootImpl = legacyRoot ? createLegacyRoot : createConcurrentRoot
186-
root = createRootImpl(container, {hydrate})
197+
root = createRootImpl(container, {hydrate, ui, wrapper})
187198

188199
mountedRootEntries.push({container, root})
189200
// we'll add it to the mounted containers regardless of whether it's actually

0 commit comments

Comments
 (0)