Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

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‎

Lines changed: 3 additions & 3 deletions
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‎

Lines changed: 35 additions & 1 deletion
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‎

Lines changed: 19 additions & 8 deletions
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)

AltStyle によって変換されたページ (->オリジナル) /