Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Throw more specific error if passed undefined in React.cloneElement #12534

Merged
7 changes: 7 additions & 0 deletions packages/react/src/ReactElement.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/

import invariant from 'fbjs/lib/invariant';
import warning from 'fbjs/lib/warning';
import {REACT_ELEMENT_TYPE} from 'shared/ReactSymbols';

Expand Down Expand Up @@ -290,6 +291,12 @@ export function cloneAndReplaceKey(oldElement, newKey) {
* See https://reactjs.org/docs/react-api.html#cloneelement
*/
export function cloneElement(element, config, children) {
invariant(
!(element === null || element === undefined),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could just use a loose equality check here

element != null

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah right yes, because the type conversion will catch undefined 👍

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, we've intentionally stopped using loose checks a while ago because they sometimes cause engine deoptimizations. I'd prefer strict ones.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gaearon didn't know that. We still have tons of places where we use loose equality checks.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gaearon okay gotcha, reverted that.

@aweary I can look into switching some over in a PR maybe? That should be pretty straightforward and I would love the opportunity to get more familiar with the codebase.

Copy link
Contributor

@aweary aweary Apr 6, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nicolevy I don't know, I'm not sure it's worth it? It could be helpful to make the changes and then somehow benchmark them. I'd also be interested in seeing a profile that shows the current deoptimizations and their impact as well. Making all the checks strict would increase the overall bundle size, so there are other costs to consider.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aweary hmmm okay, I’ll wait until a decision is made before I start on that then.

'React.cloneElement(...): The argument must be a React element, but you passed %s.',
element,
);

let propName;

// Original props are copied
Expand Down
14 changes: 14 additions & 0 deletions packages/react/src/__tests__/ReactElementClone-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -359,4 +359,18 @@ describe('ReactElementClone', () => {
}
expect(clone.props).toEqual({foo: 'ef'});
});

it('throws an error if passed null', () => {
const element = null;
expect(() => React.cloneElement(element)).toThrow(
'React.cloneElement(...): The argument must be a React element, but you passed null.',
);
});

it('throws an error if passed undefined', () => {
let element;
expect(() => React.cloneElement(element)).toThrow(
'React.cloneElement(...): The argument must be a React element, but you passed undefined.',
);
});
});