@samitouri / QOS-React / commits / 2228f497f3

Keep autoFocus attribute in the DOM (#11192)

* Keep autoFocus attribute in the DOM * Don't emit autoFocus attribute on the client * Test that hydration doesn't call focus * Add autoFocus to SSR fixture

Dan Abramov committed Oct 11, 2017 at 18:52 UTC 2228f497f34e623367796a3db03d6582b36fb2f9
5 files changed +39 -1
fixtures/ssr/src/components/Page.js
+12
@@ -2,6 +2,11 @@ import React, {Component} from 'react';
2
3 import './Page.css';
4
5 +const autofocusedInputs = [
6 + <input key="0" autoFocus placeholder="Has auto focus" />,
7 + <input key="1" autoFocus placeholder="Has auto focus" />,
8 +];
9 +
10 export default class Page extends Component {
11 state = {active: false};
12 handleClick = e => {
@@ -18,9 +23,16 @@ export default class Page extends Component {
23 <p suppressHydrationWarning={true}>
24 A random number: {Math.random()}
25 </p>
26 + <p>
27 + Autofocus on page load: {autofocusedInputs}
28 + </p>
29 <p>
30 {!this.state.active ? link : 'Thanks!'}
31 </p>
32 + {this.state.active &&
33 + <p>
34 + Autofocus on update: {autofocusedInputs}
35 + </p>}
36 </div>
37 );
38 }
src/renderers/dom/fiber/ReactDOMFiberComponent.js
+6
@@ -54,6 +54,7 @@ var registrationNameModules = EventPluginRegistry.registrationNameModules;
54 var DANGEROUSLY_SET_INNER_HTML = 'dangerouslySetInnerHTML';
55 var SUPPRESS_CONTENT_EDITABLE_WARNING = 'suppressContentEditableWarning';
56 var SUPPRESS_HYDRATION_WARNING = 'suppressHydrationWarning';
57 +var AUTOFOCUS = 'autoFocus';
58 var CHILDREN = 'children';
59 var STYLE = 'style';
60 var HTML = '__html';
@@ -286,6 +287,9 @@ function setInitialDOMProperties(
287 propKey === SUPPRESS_HYDRATION_WARNING
288 ) {
289 // Noop
290 + } else if (propKey === AUTOFOCUS) {
291 + // We polyfill it separately on the client during commit.
292 + // We blacklist it here rather than in the property list because we emit it in SSR.
293 } else if (registrationNameModules.hasOwnProperty(propKey)) {
294 if (nextProp != null) {
295 if (__DEV__ && typeof nextProp !== 'function') {
@@ -681,6 +685,8 @@ var ReactDOMFiberComponent = {
685 propKey === SUPPRESS_HYDRATION_WARNING
686 ) {
687 // Noop
688 + } else if (propKey === AUTOFOCUS) {
689 + // Noop. It doesn't work on updates anyway.
690 } else if (registrationNameModules.hasOwnProperty(propKey)) {
691 // This is a special case. If any listener updates we need to ensure
692 // that the "current" fiber pointer gets updated so we need a commit
src/renderers/dom/shared/DOMProperty.js
-1
@@ -16,7 +16,6 @@ var invariant = require('fbjs/lib/invariant');
16 var RESERVED_PROPS = {
17 children: true,
18 dangerouslySetInnerHTML: true,
19 - autoFocus: true,
19 defaultValue: true,
20 defaultChecked: true,
21 innerHTML: true,
src/renderers/dom/shared/HTMLDOMPropertyConfig.js
+1
@@ -26,6 +26,7 @@ var HTMLDOMPropertyConfig = {
26 // name warnings.
27 Properties: {
28 allowFullScreen: HAS_BOOLEAN_VALUE,
29 + autoFocus: HAS_STRING_BOOLEAN_VALUE,
30 // specifies target context for links with `preload` type
31 async: HAS_BOOLEAN_VALUE,
32 // autoFocus is polyfilled/normalized by AutoFocusUtils
src/renderers/dom/shared/__tests__/ReactServerRendering-test.js
+20
@@ -351,6 +351,26 @@ describe('ReactDOMServer', () => {
351 expect(numClicks).toEqual(2);
352 });
353
354 + // We have a polyfill for autoFocus on the client, but we intentionally don't
355 + // want it to call focus() when hydrating because this can mess up existing
356 + // focus before the JS has loaded.
357 + it('should emit autofocus on the server but not focus() when hydrating', () => {
358 + var element = document.createElement('div');
359 + element.innerHTML = ReactDOMServer.renderToString(
360 + <input autoFocus={true} />,
361 + );
362 + expect(element.firstChild.autofocus).toBe(true);
363 +
364 + // It should not be called on mount.
365 + element.firstChild.focus = jest.fn();
366 + ReactDOM.hydrate(<input autoFocus={true} />, element);
367 + expect(element.firstChild.focus).not.toHaveBeenCalled();
368 +
369 + // Or during an update.
370 + ReactDOM.render(<input autoFocus={true} />, element);
371 + expect(element.firstChild.focus).not.toHaveBeenCalled();
372 + });
373 +
374 it('should throw with silly args', () => {
375 expect(
376 ReactDOMServer.renderToString.bind(ReactDOMServer, {x: 123}),