Skip to content

Commit 142f5d5

Browse files
refactor(utilities): memoize constant PRESERVE_CUSTOM_ATTRIBUTES
Replace `utilities.reactSupportsUnknownAttributes()` with constant `utilities.PRESERVE_CUSTOM_ATTRIBUTES` to optimize performance. And update tests.
1 parent 1017b25 commit 142f5d5

File tree

6 files changed

+84
-91
lines changed

6 files changed

+84
-91
lines changed

lib/attributes-to-props.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ function attributesToProps(attributes) {
4949
reactProperty = config.svg[propertyName];
5050
if (reactProperty) {
5151
props[reactProperty] = propertyValue;
52-
} else if (utilities.reactSupportsUnknownAttributes()) {
52+
} else if (utilities.PRESERVE_CUSTOM_ATTRIBUTES) {
5353
props[propertyName] = propertyValue;
5454
}
5555
}

lib/dom-to-react.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ function domToReact(nodes, options) {
9090

9191
function shouldPassAttributesUnaltered(node) {
9292
return (
93-
utilities.reactSupportsUnknownAttributes() &&
93+
utilities.PRESERVE_CUSTOM_ATTRIBUTES &&
9494
node.type === 'tag' &&
9595
utilities.isCustomComponent(node.name, node.attribs)
9696
);

lib/utilities.js

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ function invertObject(obj, override) {
6565
/**
6666
* Check if a given tag is a custom component.
6767
*
68-
* @see https://github.com/facebook/react/blob/master/packages/react-dom/src/shared/isCustomComponent.js
68+
* @see {@link https://github.com/facebook/react/blob/v16.6.3/packages/react-dom/src/shared/isCustomComponent.js}
69+
*
6970
* @param {string} tagName - The name of the html tag.
7071
* @param {Object} props - The props being passed to the element.
7172
* @return {boolean}
@@ -74,6 +75,7 @@ function isCustomComponent(tagName, props) {
7475
if (tagName.indexOf('-') === -1) {
7576
return props && typeof props.is === 'string';
7677
}
78+
7779
switch (tagName) {
7880
// These are reserved SVG and MathML elements.
7981
// We don't mind this whitelist too much because we expect it to never grow.
@@ -94,20 +96,14 @@ function isCustomComponent(tagName, props) {
9496
}
9597

9698
/**
97-
* Check if the installed React version supports setting unknown attributes
98-
* on elements.
99-
*
100-
* @return {boolean}
99+
* @constant {Boolean}
100+
* @see {@link https://reactjs.org/blog/2017/09/08/dom-attributes-in-react-16.html}
101101
*/
102-
function reactSupportsUnknownAttributes() {
103-
const majorStr = React.version.split('.')[0];
104-
const major = parseInt(majorStr);
105-
return major >= 16;
106-
}
102+
var PRESERVE_CUSTOM_ATTRIBUTES = React.version.split('.')[0] >= 16;
107103

108104
module.exports = {
105+
PRESERVE_CUSTOM_ATTRIBUTES: PRESERVE_CUSTOM_ATTRIBUTES,
109106
camelCase: camelCase,
110107
invertObject: invertObject,
111-
isCustomComponent: isCustomComponent,
112-
reactSupportsUnknownAttributes: reactSupportsUnknownAttributes
108+
isCustomComponent: isCustomComponent
113109
};

test/attributes-to-props.js

Lines changed: 33 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,8 @@
1-
const React = require('react');
21
const assert = require('assert');
32
const attributesToProps = require('../lib/attributes-to-props');
3+
const utilities = require('../lib/utilities');
44

55
describe('attributesToProps', () => {
6-
let actualReactVersion;
7-
beforeEach(() => {
8-
actualReactVersion = React.version;
9-
});
10-
11-
afterEach(() => {
12-
React.version = actualReactVersion;
13-
});
14-
156
describe('HTML DOM', () => {
167
it('converts attributes to React props', () => {
178
assert.deepEqual(
@@ -118,16 +109,6 @@ describe('attributesToProps', () => {
118109
}
119110
);
120111
});
121-
122-
it('does not include unknown attributes for older react versions', () => {
123-
React.version = '0.14';
124-
assert.deepEqual(
125-
attributesToProps({
126-
unknownAttribute: 'someValue'
127-
}),
128-
{}
129-
);
130-
});
131112
});
132113

133114
describe('SVG DOM properties', () => {
@@ -168,18 +149,6 @@ describe('attributesToProps', () => {
168149
}
169150
);
170151
});
171-
172-
it('does not include incorrectly capitalized properties on older React versions', () => {
173-
React.version = '0.14';
174-
assert.deepEqual(
175-
attributesToProps({
176-
'XLINK:HREF': '#',
177-
ychannelselector: 'G',
178-
ZoomAndPan: 'disable'
179-
}),
180-
{}
181-
);
182-
});
183152
});
184153

185154
describe('style', () => {
@@ -249,4 +218,36 @@ describe('attributesToProps', () => {
249218
);
250219
});
251220
});
221+
222+
describe('when utilties.PRESERVE_CUSTOM_ATTRIBUTES=false', () => {
223+
const { PRESERVE_CUSTOM_ATTRIBUTES } = utilities;
224+
225+
before(() => {
226+
utilities.PRESERVE_CUSTOM_ATTRIBUTES = false;
227+
});
228+
229+
after(() => {
230+
utilities.PRESERVE_CUSTOM_ATTRIBUTES = PRESERVE_CUSTOM_ATTRIBUTES;
231+
});
232+
233+
it('does not include unknown attributes', () => {
234+
assert.deepEqual(
235+
attributesToProps({
236+
unknownAttribute: 'someValue'
237+
}),
238+
{}
239+
);
240+
});
241+
242+
it('does not include incorrectly capitalized properties', () => {
243+
assert.deepEqual(
244+
attributesToProps({
245+
'XLINK:HREF': '#',
246+
ychannelselector: 'G',
247+
ZoomAndPan: 'disable'
248+
}),
249+
{}
250+
);
251+
});
252+
});
252253
});

test/dom-to-react.js

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const React = require('react');
33
const htmlToDOM = require('html-dom-parser');
44
const domToReact = require('../lib/dom-to-react');
55
const { data, render } = require('./helpers/');
6+
const utilities = require('../lib/utilities');
67

78
describe('dom-to-react parser', () => {
89
let actualReactVersion;
@@ -141,7 +142,7 @@ describe('dom-to-react parser', () => {
141142
);
142143
});
143144

144-
it('passes props unaltered for custom elements', () => {
145+
it('does not modify attributes on custom elements', () => {
145146
const html = data.html.customElement;
146147
const reactElement = domToReact(htmlToDOM(html));
147148

@@ -158,14 +159,25 @@ describe('dom-to-react parser', () => {
158159
);
159160
});
160161

161-
it('handles custom element the same as everything else with older reacts', () => {
162-
React.version = '0.14';
163-
const html = data.html.customElement;
164-
const reactElement = domToReact(htmlToDOM(html));
162+
describe('when React <16', () => {
163+
const { PRESERVE_CUSTOM_ATTRIBUTES } = utilities;
165164

166-
assert.deepEqual(
167-
reactElement,
168-
React.createElement('custom-button', { className: 'myClass' }, null)
169-
);
165+
before(() => {
166+
utilities.PRESERVE_CUSTOM_ATTRIBUTES = false;
167+
});
168+
169+
after(() => {
170+
utilities.PRESERVE_CUSTOM_ATTRIBUTES = PRESERVE_CUSTOM_ATTRIBUTES;
171+
});
172+
173+
it('modifies attributes on custom elements', () => {
174+
const html = data.html.customElement;
175+
const reactElement = domToReact(htmlToDOM(html));
176+
177+
assert.deepEqual(
178+
reactElement,
179+
React.createElement('custom-button', { className: 'myClass' }, null)
180+
);
181+
});
170182
});
171183
});

test/utilities.js

Lines changed: 20 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
const React = require('react');
21
const assert = require('assert');
2+
const React = require('react');
33
const {
4+
PRESERVE_CUSTOM_ATTRIBUTES,
45
camelCase,
56
invertObject,
6-
isCustomComponent,
7-
reactSupportsUnknownAttributes
7+
isCustomComponent
88
} = require('../lib/utilities');
99

1010
describe('utilties.camelCase', () => {
@@ -86,44 +86,28 @@ describe('utilities.invertObject', () => {
8686
);
8787
});
8888
});
89+
});
8990

90-
describe('utilities.isCustomComponent', () => {
91-
it('returns true if the tag contains a hyphen and is not in the whitelist', () => {
92-
assert.equal(isCustomComponent('my-custom-element'), true);
93-
});
94-
95-
it('returns false if the tag is in the whitelist', () => {
96-
assert.equal(isCustomComponent('annotation-xml'), false);
97-
assert.equal(isCustomComponent('color-profile'), false);
98-
assert.equal(isCustomComponent('font-face'), false);
99-
});
100-
101-
it('returns true if the props contains an `is` key', () => {
102-
assert.equal(isCustomComponent('button', { is: 'custom-button' }), true);
103-
});
91+
describe('utilities.isCustomComponent', () => {
92+
it('returns true if the tag contains a hyphen and is not in the whitelist', () => {
93+
assert.equal(isCustomComponent('my-custom-element'), true);
10494
});
10595

106-
describe('utilities.reactSupportsUnknownAttributes', () => {
107-
let actualVersion;
108-
beforeEach(() => {
109-
actualVersion = React.version;
110-
});
111-
112-
afterEach(() => {
113-
React.version = actualVersion;
114-
});
96+
it('returns false if the tag is in the whitelist', () => {
97+
assert.equal(isCustomComponent('annotation-xml'), false);
98+
assert.equal(isCustomComponent('color-profile'), false);
99+
assert.equal(isCustomComponent('font-face'), false);
100+
});
115101

116-
it('should return true for React 16 and above', () => {
117-
React.version = '16.6.0';
118-
assert.equal(reactSupportsUnknownAttributes(), true);
119-
});
102+
it('returns true if the props contains an `is` key', () => {
103+
assert.equal(isCustomComponent('button', { is: 'custom-button' }), true);
104+
});
105+
});
120106

121-
it('should return false for React < 16', () => {
122-
React.version = '15.1.2';
123-
assert.equal(reactSupportsUnknownAttributes(), false);
107+
describe('utilities.PRESERVE_CUSTOM_ATTRIBUTES', () => {
108+
const isReact16AndUp = Number(React.version.match(/^\d./)[0]) >= 16;
124109

125-
React.version = '0.14';
126-
assert.equal(reactSupportsUnknownAttributes(), false);
127-
});
110+
it(`is ${isReact16AndUp} when React.version="${React.version}"`, () => {
111+
assert.equal(PRESERVE_CUSTOM_ATTRIBUTES, isReact16AndUp);
128112
});
129113
});

0 commit comments

Comments
 (0)