Fix autoFocus for hydration content when it is mismatched (#11737)
* Fix autoFocus for hydration content when it is mismatched * Add a test for mismatched content * Fix a test for production * Fix a spec description and verify console.error output * Run prettier * finalizeInitialChildren always returns `true` * Revert "finalizeInitialChildren always returns `true`" This reverts commit 58edd228046bcafcbcd04a70cb5e78520b50a07e. * Add a TODO comment * Update ReactServerRendering-test.js * Update ReactServerRendering-test.js * Rewrite the comment
Toru Kobayashi committed
Dec 7, 2017 at 00:23 UTC
19bc2dd090c44be862bae59c64e89acb8d0827f5
2 files changed
+35
-5
packages/react-dom/src/__tests__/ReactServerRendering-test.js
+22
@@ -372,6 +372,28 @@ describe('ReactDOMServer', () => {
372
expect(element.firstChild.focus).not.toHaveBeenCalled();
373
});
374
375
+ // Regression test for https://github.com/facebook/react/issues/11726
376
+ it('should not focus on either server or client with autofocus={false} even if there is a markup mismatch', () => {
377
+ spyOnDev(console, 'error');
378
+
379
+ var element = document.createElement('div');
380
+ element.innerHTML = ReactDOMServer.renderToString(
381
+ <button autoFocus={false}>server</button>,
382
+ );
383
+ expect(element.firstChild.autofocus).toBe(false);
384
+
385
+ element.firstChild.focus = jest.fn();
386
+ ReactDOM.hydrate(<button autoFocus={false}>client</button>, element);
387
+
388
+ expect(element.firstChild.focus).not.toHaveBeenCalled();
389
+ if (__DEV__) {
390
+ expect(console.error.calls.count()).toBe(1);
391
+ expect(console.error.calls.argsFor(0)[0]).toBe(
392
+ 'Warning: Text content did not match. Server: "server" Client: "client"',
393
+ );
394
+ }
395
+ });
396
+
397
it('should throw with silly args', () => {
398
expect(
399
ReactDOMServer.renderToString.bind(ReactDOMServer, {x: 123}),
packages/react-dom/src/client/ReactDOM.js
+13
-5
@@ -704,11 +704,19 @@ const DOMRenderer = ReactFiberReconciler({
704
newProps: Props,
705
internalInstanceHandle: Object,
706
): void {
707
- ((domElement: any):
708
- | HTMLButtonElement
709
- | HTMLInputElement
710
- | HTMLSelectElement
711
- | HTMLTextAreaElement).focus();
707
+ // Despite the naming that might imply otherwise, this method only
708
+ // fires if there is an `Update` effect scheduled during mounting.
709
+ // This happens if `finalizeInitialChildren` returns `true` (which it
710
+ // does to implement the `autoFocus` attribute on the client). But
711
+ // there are also other cases when this might happen (such as patching
712
+ // up text content during hydration mismatch). So we'll check this again.
713
+ if (shouldAutoFocusHostComponent(type, newProps)) {
714
+ ((domElement: any):
715
+ | HTMLButtonElement
716
+ | HTMLInputElement
717
+ | HTMLSelectElement
718
+ | HTMLTextAreaElement).focus();
719
+ }
720
},
721
722
commitUpdate(