Skip to content

Commit 2228f49

Browse files
authored
Keep autoFocus attribute in the DOM (facebook#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
1 parent b75be6a commit 2228f49

File tree

5 files changed

+39
-1
lines changed

5 files changed

+39
-1
lines changed

fixtures/ssr/src/components/Page.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@ import React, {Component} from 'react';
22

33
import './Page.css';
44

5+
const autofocusedInputs = [
6+
<input key="0" autoFocus placeholder="Has auto focus" />,
7+
<input key="1" autoFocus placeholder="Has auto focus" />,
8+
];
9+
510
export default class Page extends Component {
611
state = {active: false};
712
handleClick = e => {
@@ -18,9 +23,16 @@ export default class Page extends Component {
1823
<p suppressHydrationWarning={true}>
1924
A random number: {Math.random()}
2025
</p>
26+
<p>
27+
Autofocus on page load: {autofocusedInputs}
28+
</p>
2129
<p>
2230
{!this.state.active ? link : 'Thanks!'}
2331
</p>
32+
{this.state.active &&
33+
<p>
34+
Autofocus on update: {autofocusedInputs}
35+
</p>}
2436
</div>
2537
);
2638
}

src/renderers/dom/fiber/ReactDOMFiberComponent.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ var registrationNameModules = EventPluginRegistry.registrationNameModules;
5454
var DANGEROUSLY_SET_INNER_HTML = 'dangerouslySetInnerHTML';
5555
var SUPPRESS_CONTENT_EDITABLE_WARNING = 'suppressContentEditableWarning';
5656
var SUPPRESS_HYDRATION_WARNING = 'suppressHydrationWarning';
57+
var AUTOFOCUS = 'autoFocus';
5758
var CHILDREN = 'children';
5859
var STYLE = 'style';
5960
var HTML = '__html';
@@ -286,6 +287,9 @@ function setInitialDOMProperties(
286287
propKey === SUPPRESS_HYDRATION_WARNING
287288
) {
288289
// 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.
289293
} else if (registrationNameModules.hasOwnProperty(propKey)) {
290294
if (nextProp != null) {
291295
if (__DEV__ && typeof nextProp !== 'function') {
@@ -681,6 +685,8 @@ var ReactDOMFiberComponent = {
681685
propKey === SUPPRESS_HYDRATION_WARNING
682686
) {
683687
// Noop
688+
} else if (propKey === AUTOFOCUS) {
689+
// Noop. It doesn't work on updates anyway.
684690
} else if (registrationNameModules.hasOwnProperty(propKey)) {
685691
// This is a special case. If any listener updates we need to ensure
686692
// that the "current" fiber pointer gets updated so we need a commit

src/renderers/dom/shared/DOMProperty.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ var invariant = require('fbjs/lib/invariant');
1616
var RESERVED_PROPS = {
1717
children: true,
1818
dangerouslySetInnerHTML: true,
19-
autoFocus: true,
2019
defaultValue: true,
2120
defaultChecked: true,
2221
innerHTML: true,

src/renderers/dom/shared/HTMLDOMPropertyConfig.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ var HTMLDOMPropertyConfig = {
2626
// name warnings.
2727
Properties: {
2828
allowFullScreen: HAS_BOOLEAN_VALUE,
29+
autoFocus: HAS_STRING_BOOLEAN_VALUE,
2930
// specifies target context for links with `preload` type
3031
async: HAS_BOOLEAN_VALUE,
3132
// autoFocus is polyfilled/normalized by AutoFocusUtils

src/renderers/dom/shared/__tests__/ReactServerRendering-test.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,26 @@ describe('ReactDOMServer', () => {
351351
expect(numClicks).toEqual(2);
352352
});
353353

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+
354374
it('should throw with silly args', () => {
355375
expect(
356376
ReactDOMServer.renderToString.bind(ReactDOMServer, {x: 123}),

0 commit comments

Comments
 (0)