Skip to content

Commit 76a760f

Browse files
EmmaSimongajus
authored andcommitted
fix: React 16 issues (fixes gajus#269, #200, #259)
* Check if both element and props are frozen or not extensible (#200) Fixes #200 * Only check extensiblity of props Also run preventExtensions on it after modifications * Check if just the props are frozen (#266) * Recursively map element arrays Fixes #259 * Add tests For element arrays and frozen elements/props
1 parent 1952fb5 commit 76a760f

File tree

2 files changed

+90
-5
lines changed

2 files changed

+90
-5
lines changed

src/linkClass.js

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,26 @@ const linkArray = (array: Array, styles: Object, configuration: Object) => {
2222

2323
const linkElement = (element: ReactElement, styles: Object, configuration: Object): ReactElement => {
2424
let appendClassName;
25-
let elementIsFrozen;
2625
let elementShallowCopy;
2726

2827
elementShallowCopy = element;
2928

30-
if (Object.isFrozen && Object.isFrozen(elementShallowCopy)) {
31-
elementIsFrozen = true;
29+
if (Array.isArray(elementShallowCopy)) {
30+
return elementShallowCopy.map((arrayElement) => {
31+
return linkElement(arrayElement, styles, configuration);
32+
});
33+
}
3234

33-
// https://github.com/facebook/react/blob/v0.13.3/src/classic/element/ReactElement.js#L131
35+
const elementIsFrozen = Object.isFrozen && Object.isFrozen(elementShallowCopy);
36+
const propsFrozen = Object.isFrozen && Object.isFrozen(elementShallowCopy.props);
37+
const propsNotExtensible = Object.isExtensible && !Object.isExtensible(elementShallowCopy.props);
38+
39+
if (elementIsFrozen) {
40+
// https://github.com/facebook/react/blob/v0.13.3/src/classic/element/ReactElement.js#L131
3441
elementShallowCopy = objectUnfreeze(elementShallowCopy);
3542
elementShallowCopy.props = objectUnfreeze(elementShallowCopy.props);
43+
} else if (propsFrozen || propsNotExtensible) {
44+
elementShallowCopy.props = objectUnfreeze(elementShallowCopy.props);
3645
}
3746

3847
const styleNames = parseStyleName(elementShallowCopy.props.styleName || '', configuration.allowMultiple);
@@ -76,6 +85,12 @@ const linkElement = (element: ReactElement, styles: Object, configuration: Objec
7685
if (elementIsFrozen) {
7786
Object.freeze(elementShallowCopy.props);
7887
Object.freeze(elementShallowCopy);
88+
} else if (propsFrozen) {
89+
Object.freeze(elementShallowCopy.props);
90+
}
91+
92+
if (propsNotExtensible) {
93+
Object.preventExtensions(elementShallowCopy.props);
7994
}
8095

8196
return elementShallowCopy;

tests/linkClass.js

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* eslint-disable max-nested-callbacks, react/prefer-stateless-function, class-methods-use-this, no-console */
1+
/* eslint-disable max-nested-callbacks, react/prefer-stateless-function, class-methods-use-this, no-console, no-unused-expressions */
22

33
import chai, {
44
expect
@@ -239,6 +239,76 @@ describe('linkClass', () => {
239239
});
240240
});
241241

242+
context('can\'t write to properties', () => {
243+
context('when the element is frozen', () => {
244+
it('adds className but is still frozen', () => {
245+
let subject;
246+
247+
subject = <div styleName='foo' />;
248+
249+
Object.freeze(subject);
250+
subject = linkClass(subject, {
251+
foo: 'foo-1'
252+
});
253+
254+
expect(subject).to.be.frozen;
255+
expect(subject.props.className).to.equal('foo-1');
256+
});
257+
});
258+
context('when the element\'s props are frozen', () => {
259+
it('adds className and only props are still frozen', () => {
260+
let subject;
261+
262+
subject = <div styleName='foo' />;
263+
264+
Object.freeze(subject.props);
265+
subject = linkClass(subject, {
266+
foo: 'foo-1'
267+
});
268+
269+
expect(subject.props).to.be.frozen;
270+
expect(subject.props.className).to.equal('foo-1');
271+
});
272+
});
273+
context('when the element\'s props are not extensible', () => {
274+
it('adds className and props are still not extensible', () => {
275+
let subject;
276+
277+
subject = <div styleName='foo' />;
278+
279+
Object.preventExtensions(subject.props);
280+
subject = linkClass(subject, {
281+
foo: 'foo-1'
282+
});
283+
284+
expect(subject.props).to.not.be.extensible;
285+
expect(subject.props.className).to.equal('foo-1');
286+
});
287+
});
288+
});
289+
290+
context('when element is an array', () => {
291+
it('handles each element individually', () => {
292+
let subject;
293+
294+
subject = [
295+
<div key={1} styleName='foo' />,
296+
<div key={2}>
297+
<p styleName='bar' />
298+
</div>
299+
];
300+
301+
subject = linkClass(subject, {
302+
bar: 'bar-1',
303+
foo: 'foo-1'
304+
});
305+
306+
expect(subject).to.be.an('array');
307+
expect(subject[0].props.className).to.equal('foo-1');
308+
expect(subject[1].props.children.props.className).to.equal('bar-1');
309+
});
310+
});
311+
242312
describe('options.allowMultiple', () => {
243313
context('when multiple module names are used', () => {
244314
context('when false', () => {

0 commit comments

Comments
 (0)