Skip to content

Commit 7cbb062

Browse files
committed
Inline mustUseProperty which is only used for special controlled components
1 parent ee1b716 commit 7cbb062

File tree

6 files changed

+172
-127
lines changed

6 files changed

+172
-127
lines changed

packages/react-dom-bindings/src/client/DOMPropertyOperations.js

-15
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,6 @@ export function getValueForProperty(
3838
propertyInfo: PropertyInfo,
3939
): mixed {
4040
if (__DEV__) {
41-
if (propertyInfo.mustUseProperty) {
42-
const {propertyName} = propertyInfo;
43-
return (node: any)[propertyName];
44-
}
45-
4641
const attributeName = propertyInfo.attributeName;
4742

4843
if (!node.hasAttribute(attributeName)) {
@@ -292,16 +287,6 @@ export function setValueForProperty(
292287
propertyInfo: PropertyInfo,
293288
value: mixed,
294289
) {
295-
if (propertyInfo.mustUseProperty) {
296-
// We assume mustUseProperty are of BOOLEAN type because that's the only way we use it
297-
// right now.
298-
(node: any)[propertyInfo.propertyName] =
299-
value && typeof value !== 'function' && typeof value !== 'symbol';
300-
return;
301-
}
302-
303-
// The rest are treated as attributes with special cases.
304-
305290
const attributeName = propertyInfo.attributeName;
306291

307292
if (value === null) {

packages/react-dom-bindings/src/client/ReactDOMComponent.js

+61-2
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,18 @@ function setProp(
379379
}
380380
break;
381381
}
382+
// Note: `option.selected` is not updated if `select.multiple` is
383+
// disabled with `removeAttribute`. We have special logic for handling this.
384+
case 'multiple': {
385+
(domElement: any).multiple =
386+
value && typeof value !== 'function' && typeof value !== 'symbol';
387+
break;
388+
}
389+
case 'muted': {
390+
(domElement: any).muted =
391+
value && typeof value !== 'function' && typeof value !== 'symbol';
392+
break;
393+
}
382394
case 'suppressContentEditableWarning':
383395
case 'suppressHydrationWarning':
384396
case 'defaultValue': // Reserved
@@ -703,7 +715,19 @@ export function setInitialProperties(
703715
if (propValue == null) {
704716
continue;
705717
}
706-
setProp(domElement, tag, propKey, propValue, false, props);
718+
switch (propKey) {
719+
case 'selected': {
720+
// TODO: Remove support for selected on option.
721+
(domElement: any).selected =
722+
propValue &&
723+
typeof propValue !== 'function' &&
724+
typeof propValue !== 'symbol';
725+
break;
726+
}
727+
default: {
728+
setProp(domElement, tag, propKey, propValue, false, props);
729+
}
730+
}
707731
}
708732
ReactDOMOptionPostMountWrapper(domElement, props);
709733
return;
@@ -1018,6 +1042,26 @@ export function updateProperties(
10181042
ReactDOMTextareaUpdateWrapper(domElement, nextProps);
10191043
return;
10201044
}
1045+
case 'option': {
1046+
for (let i = 0; i < updatePayload.length; i += 2) {
1047+
const propKey = updatePayload[i];
1048+
const propValue = updatePayload[i + 1];
1049+
switch (propKey) {
1050+
case 'selected': {
1051+
// TODO: Remove support for selected on option.
1052+
(domElement: any).selected =
1053+
propValue &&
1054+
typeof propValue !== 'function' &&
1055+
typeof propValue !== 'symbol';
1056+
break;
1057+
}
1058+
default: {
1059+
setProp(domElement, tag, propKey, propValue, false, nextProps);
1060+
}
1061+
}
1062+
}
1063+
return;
1064+
}
10211065
case 'img':
10221066
case 'link':
10231067
case 'area':
@@ -1249,7 +1293,22 @@ function diffHydratedGenericElement(
12491293
extraAttributeNames.delete(propKey);
12501294
diffHydratedStyles(domElement, nextProp);
12511295
continue;
1252-
// eslint-disable-next-line no-fallthrough
1296+
case 'multiple': {
1297+
extraAttributeNames.delete(propKey);
1298+
const serverValue = (domElement: any).multiple;
1299+
if (nextProp !== serverValue) {
1300+
warnForPropDifference('multiple', serverValue, nextProp);
1301+
}
1302+
continue;
1303+
}
1304+
case 'muted': {
1305+
extraAttributeNames.delete(propKey);
1306+
const serverValue = (domElement: any).muted;
1307+
if (nextProp !== serverValue) {
1308+
warnForPropDifference('muted', serverValue, nextProp);
1309+
}
1310+
continue;
1311+
}
12531312
default:
12541313
if (
12551314
// shouldIgnoreAttribute

packages/react-dom-bindings/src/server/ReactDOMServerFormatConfig.js

+16-2
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,16 @@ const attributeAssign = stringToPrecomputedChunk('="');
611611
const attributeEnd = stringToPrecomputedChunk('"');
612612
const attributeEmptyString = stringToPrecomputedChunk('=""');
613613

614+
function pushBooleanAttribute(
615+
target: Array<Chunk | PrecomputedChunk>,
616+
name: string,
617+
value: string | boolean | number | Function | Object, // not null or undefined
618+
): void {
619+
if (value && typeof value !== 'function' && typeof value !== 'symbol') {
620+
target.push(attributeSeparator, stringToChunk(name), attributeEmptyString);
621+
}
622+
}
623+
614624
function pushAttribute(
615625
target: Array<Chunk | PrecomputedChunk>,
616626
name: string,
@@ -628,6 +638,10 @@ function pushAttribute(
628638
case 'suppressHydrationWarning':
629639
// Ignored. These are built-in to React on the client.
630640
return;
641+
case 'multiple':
642+
case 'muted':
643+
pushBooleanAttribute(target, name, value);
644+
return;
631645
}
632646
if (
633647
// shouldIgnoreAttribute
@@ -1112,9 +1126,9 @@ function pushInput(
11121126
}
11131127

11141128
if (checked !== null) {
1115-
pushAttribute(target, 'checked', checked);
1129+
pushBooleanAttribute(target, 'checked', checked);
11161130
} else if (defaultChecked !== null) {
1117-
pushAttribute(target, 'checked', defaultChecked);
1131+
pushBooleanAttribute(target, 'checked', defaultChecked);
11181132
}
11191133
if (value !== null) {
11201134
pushAttribute(target, 'value', value);

packages/react-dom-bindings/src/shared/DOMProperty.js

-60
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,6 @@ export type PropertyInfo = {
4242
+acceptsBooleans: boolean,
4343
+attributeName: string,
4444
+attributeNamespace: string | null,
45-
+mustUseProperty: boolean,
46-
+propertyName: string,
4745
+type: PropertyType,
4846
+sanitizeURL: boolean,
4947
+removeEmptyString: boolean,
@@ -55,9 +53,7 @@ export function getPropertyInfo(name: string): PropertyInfo | null {
5553

5654
// $FlowFixMe[missing-this-annot]
5755
function PropertyInfoRecord(
58-
name: string,
5956
type: PropertyType,
60-
mustUseProperty: boolean,
6157
attributeName: string,
6258
attributeNamespace: string | null,
6359
sanitizeURL: boolean,
@@ -69,8 +65,6 @@ function PropertyInfoRecord(
6965
type === OVERLOADED_BOOLEAN;
7066
this.attributeName = attributeName;
7167
this.attributeNamespace = attributeNamespace;
72-
this.mustUseProperty = mustUseProperty;
73-
this.propertyName = name;
7468
this.type = type;
7569
this.sanitizeURL = sanitizeURL;
7670
this.removeEmptyString = removeEmptyString;
@@ -91,9 +85,7 @@ const properties: {[string]: $FlowFixMe} = {};
9185
].forEach(([name, attributeName]) => {
9286
// $FlowFixMe[invalid-constructor] Flow no longer supports calling new on functions
9387
properties[name] = new PropertyInfoRecord(
94-
name,
9588
STRING,
96-
false, // mustUseProperty
9789
attributeName, // attributeName
9890
null, // attributeNamespace
9991
false, // sanitizeURL
@@ -107,9 +99,7 @@ const properties: {[string]: $FlowFixMe} = {};
10799
['contentEditable', 'draggable', 'spellCheck', 'value'].forEach(name => {
108100
// $FlowFixMe[invalid-constructor] Flow no longer supports calling new on functions
109101
properties[name] = new PropertyInfoRecord(
110-
name,
111102
BOOLEANISH_STRING,
112-
false, // mustUseProperty
113103
name.toLowerCase(), // attributeName
114104
null, // attributeNamespace
115105
false, // sanitizeURL
@@ -129,9 +119,7 @@ const properties: {[string]: $FlowFixMe} = {};
129119
].forEach(name => {
130120
// $FlowFixMe[invalid-constructor] Flow no longer supports calling new on functions
131121
properties[name] = new PropertyInfoRecord(
132-
name,
133122
BOOLEANISH_STRING,
134-
false, // mustUseProperty
135123
name, // attributeName
136124
null, // attributeNamespace
137125
false, // sanitizeURL
@@ -170,42 +158,14 @@ const properties: {[string]: $FlowFixMe} = {};
170158
].forEach(name => {
171159
// $FlowFixMe[invalid-constructor] Flow no longer supports calling new on functions
172160
properties[name] = new PropertyInfoRecord(
173-
name,
174161
BOOLEAN,
175-
false, // mustUseProperty
176162
name.toLowerCase(), // attributeName
177163
null, // attributeNamespace
178164
false, // sanitizeURL
179165
false, // removeEmptyString
180166
);
181167
});
182168

183-
// These are the few React props that we set as DOM properties
184-
// rather than attributes. These are all booleans.
185-
[
186-
'checked',
187-
// Note: `option.selected` is not updated if `select.multiple` is
188-
// disabled with `removeAttribute`. We have special logic for handling this.
189-
'multiple',
190-
'muted',
191-
'selected',
192-
193-
// NOTE: if you add a camelCased prop to this list,
194-
// you'll need to set attributeName to name.toLowerCase()
195-
// instead in the assignment below.
196-
].forEach(name => {
197-
// $FlowFixMe[invalid-constructor] Flow no longer supports calling new on functions
198-
properties[name] = new PropertyInfoRecord(
199-
name,
200-
BOOLEAN,
201-
true, // mustUseProperty
202-
name, // attributeName
203-
null, // attributeNamespace
204-
false, // sanitizeURL
205-
false, // removeEmptyString
206-
);
207-
});
208-
209169
// These are HTML attributes that are "overloaded booleans": they behave like
210170
// booleans, but can also accept a string value.
211171
[
@@ -218,9 +178,7 @@ const properties: {[string]: $FlowFixMe} = {};
218178
].forEach(name => {
219179
// $FlowFixMe[invalid-constructor] Flow no longer supports calling new on functions
220180
properties[name] = new PropertyInfoRecord(
221-
name,
222181
OVERLOADED_BOOLEAN,
223-
false, // mustUseProperty
224182
name, // attributeName
225183
null, // attributeNamespace
226184
false, // sanitizeURL
@@ -241,9 +199,7 @@ const properties: {[string]: $FlowFixMe} = {};
241199
].forEach(name => {
242200
// $FlowFixMe[invalid-constructor] Flow no longer supports calling new on functions
243201
properties[name] = new PropertyInfoRecord(
244-
name,
245202
POSITIVE_NUMERIC,
246-
false, // mustUseProperty
247203
name, // attributeName
248204
null, // attributeNamespace
249205
false, // sanitizeURL
@@ -255,9 +211,7 @@ const properties: {[string]: $FlowFixMe} = {};
255211
['rowSpan', 'start'].forEach(name => {
256212
// $FlowFixMe[invalid-constructor] Flow no longer supports calling new on functions
257213
properties[name] = new PropertyInfoRecord(
258-
name,
259214
NUMERIC,
260-
false, // mustUseProperty
261215
name.toLowerCase(), // attributeName
262216
null, // attributeNamespace
263217
false, // sanitizeURL
@@ -356,9 +310,7 @@ const capitalize = (token: string) => token[1].toUpperCase();
356310
const name = attributeName.replace(CAMELIZE, capitalize);
357311
// $FlowFixMe[invalid-constructor] Flow no longer supports calling new on functions
358312
properties[name] = new PropertyInfoRecord(
359-
name,
360313
STRING,
361-
false, // mustUseProperty
362314
attributeName,
363315
null, // attributeNamespace
364316
false, // sanitizeURL
@@ -382,9 +334,7 @@ const capitalize = (token: string) => token[1].toUpperCase();
382334
const name = attributeName.replace(CAMELIZE, capitalize);
383335
// $FlowFixMe[invalid-constructor] Flow no longer supports calling new on functions
384336
properties[name] = new PropertyInfoRecord(
385-
name,
386337
STRING,
387-
false, // mustUseProperty
388338
attributeName,
389339
'http://www.w3.org/1999/xlink',
390340
false, // sanitizeURL
@@ -405,9 +355,7 @@ const capitalize = (token: string) => token[1].toUpperCase();
405355
const name = attributeName.replace(CAMELIZE, capitalize);
406356
// $FlowFixMe[invalid-constructor] Flow no longer supports calling new on functions
407357
properties[name] = new PropertyInfoRecord(
408-
name,
409358
STRING,
410-
false, // mustUseProperty
411359
attributeName,
412360
'http://www.w3.org/XML/1998/namespace',
413361
false, // sanitizeURL
@@ -421,9 +369,7 @@ const capitalize = (token: string) => token[1].toUpperCase();
421369
['tabIndex', 'crossOrigin'].forEach(attributeName => {
422370
// $FlowFixMe[invalid-constructor] Flow no longer supports calling new on functions
423371
properties[attributeName] = new PropertyInfoRecord(
424-
attributeName,
425372
STRING,
426-
false, // mustUseProperty
427373
attributeName.toLowerCase(), // attributeName
428374
null, // attributeNamespace
429375
false, // sanitizeURL
@@ -436,9 +382,7 @@ const capitalize = (token: string) => token[1].toUpperCase();
436382
const xlinkHref = 'xlinkHref';
437383
// $FlowFixMe[invalid-constructor] Flow no longer supports calling new on functions
438384
properties[xlinkHref] = new PropertyInfoRecord(
439-
'xlinkHref',
440385
STRING,
441-
false, // mustUseProperty
442386
'xlink:href',
443387
'http://www.w3.org/1999/xlink',
444388
true, // sanitizeURL
@@ -448,9 +392,7 @@ properties[xlinkHref] = new PropertyInfoRecord(
448392
const formAction = 'formAction';
449393
// $FlowFixMe[invalid-constructor] Flow no longer supports calling new on functions
450394
properties[formAction] = new PropertyInfoRecord(
451-
'formAction',
452395
STRING,
453-
false, // mustUseProperty
454396
'formaction', // attributeName
455397
null, // attributeNamespace
456398
true, // sanitizeURL
@@ -460,9 +402,7 @@ properties[formAction] = new PropertyInfoRecord(
460402
['src', 'href', 'action'].forEach(attributeName => {
461403
// $FlowFixMe[invalid-constructor] Flow no longer supports calling new on functions
462404
properties[attributeName] = new PropertyInfoRecord(
463-
attributeName,
464405
STRING,
465-
false, // mustUseProperty
466406
attributeName.toLowerCase(), // attributeName
467407
null, // attributeNamespace
468408
true, // sanitizeURL

0 commit comments

Comments
 (0)