feature: Support whitelisting mutable props for react-constant-elements (#5307)
This commit is contained in:
parent
248743e6c5
commit
e0b4543601
@ -1,6 +1,9 @@
|
||||
# babel-plugin-transform-react-constant-elements
|
||||
|
||||
> Treat React JSX elements as value types and hoist them to the highest scope
|
||||
> Treat React JSX elements as value types and hoist them to the highest scope.
|
||||
|
||||
This plugin can speed up reconciliation and reduce garbage collection pressure by hoisting
|
||||
React elements to the highest possible scope, preventing multiple unnecessary reinstantiations.
|
||||
|
||||
## Example
|
||||
|
||||
@ -37,6 +40,14 @@ const Hr = () => {
|
||||
<div ref={node => this.node = node} />
|
||||
```
|
||||
|
||||
- **Mutable Properties**
|
||||
|
||||
> See https://github.com/facebook/react/issues/3226 for more on this
|
||||
|
||||
```js
|
||||
<div width={{width: 100}} />
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
```sh
|
||||
@ -55,6 +66,26 @@ npm install --save-dev babel-plugin-transform-react-constant-elements
|
||||
}
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
### `allowMutablePropsOnTags`
|
||||
|
||||
`Array<string>`, defaults to `[]`
|
||||
|
||||
If you are using a particular library (like react-intl) that uses object properties, and you are sure
|
||||
that the element won't modify its own props, you can whitelist the element so that objects are allowed.
|
||||
|
||||
This will skip the `Mutable Properties` deopt.
|
||||
|
||||
```json
|
||||
{
|
||||
"plugins": [
|
||||
["transform-react-constant-elements", {"allowMutablePropsOnTags": ["FormattedMessage"]}],
|
||||
]
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Via CLI
|
||||
|
||||
```sh
|
||||
|
||||
@ -41,10 +41,12 @@ export default function({ types: t }) {
|
||||
// We know the result; check its mutability.
|
||||
const { value } = expressionResult;
|
||||
const isMutable =
|
||||
(value && typeof value === "object") ||
|
||||
(!state.mutablePropsAllowed &&
|
||||
(value && typeof value === "object")) ||
|
||||
typeof value === "function";
|
||||
if (!isMutable) {
|
||||
// It evaluated to an immutable value, so we can hoist it.
|
||||
path.skip();
|
||||
return;
|
||||
}
|
||||
} else if (t.isIdentifier(expressionResult.deopt)) {
|
||||
@ -65,6 +67,29 @@ export default function({ types: t }) {
|
||||
HOISTED.add(path.node);
|
||||
|
||||
const state = { isImmutable: true };
|
||||
|
||||
// This transform takes the option `allowMutablePropsOnTags`, which is an array
|
||||
// of JSX tags to allow mutable props (such as objects, functions) on. Use sparingly
|
||||
// and only on tags you know will never modify their own props.
|
||||
if (this.opts.allowMutablePropsOnTags != null) {
|
||||
if (!Array.isArray(this.opts.allowMutablePropsOnTags)) {
|
||||
throw new Error(
|
||||
".allowMutablePropsOnTags must be an array, null, or undefined.",
|
||||
);
|
||||
}
|
||||
// Get the element's name. If it's a member expression, we use the last part of the path.
|
||||
// So the option ["FormattedMessage"] would match "Intl.FormattedMessage".
|
||||
let namePath = path.get("openingElement.name");
|
||||
while (namePath.isJSXMemberExpression()) {
|
||||
namePath = namePath.get("property");
|
||||
}
|
||||
|
||||
const elementName = namePath.node.name;
|
||||
state.mutablePropsAllowed =
|
||||
this.opts.allowMutablePropsOnTags.indexOf(elementName) > -1;
|
||||
}
|
||||
|
||||
// Traverse all props passed to this element for immutability.
|
||||
path.traverse(immutabilityVisitor, state);
|
||||
|
||||
if (state.isImmutable) {
|
||||
|
||||
@ -0,0 +1,20 @@
|
||||
// This is the same as the pure-expression-whitelist test, but 'FormattedMessage' is *not* whitelisted
|
||||
// so it should not be hoisted.
|
||||
var Foo = React.createClass({
|
||||
render: function () {
|
||||
return (
|
||||
<FormattedMessage
|
||||
id="someMessage.foo"
|
||||
defaultMessage={
|
||||
"Some text, " +
|
||||
"and some more too. {someValue}"
|
||||
}
|
||||
description="A test message for babel."
|
||||
values={{
|
||||
someValue: "A value."
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
// This is the same as the pure-expression-whitelist test, but 'FormattedMessage' is *not* whitelisted
|
||||
// so it should not be hoisted.
|
||||
var Foo = React.createClass({
|
||||
render: function () {
|
||||
return <FormattedMessage id="someMessage.foo" defaultMessage={"Some text, " + "and some more too. {someValue}"} description="A test message for babel." values={{
|
||||
someValue: "A value."
|
||||
}} />;
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,7 @@
|
||||
{
|
||||
|
||||
"plugins": [
|
||||
["transform-react-constant-elements", {"allowMutablePropsOnTags": ["FormattedNumber"]}],
|
||||
"syntax-jsx"
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
import Intl from 'react-intl';
|
||||
|
||||
var Foo = React.createClass({
|
||||
render: function () {
|
||||
return (
|
||||
<Intl.FormattedMessage
|
||||
id="someMessage.foo"
|
||||
defaultMessage={
|
||||
"Some text, " +
|
||||
"and some more too. {someValue}"
|
||||
}
|
||||
description="A test message for babel."
|
||||
values={{
|
||||
someValue: "A value."
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
import Intl from 'react-intl';
|
||||
|
||||
var _ref = <Intl.FormattedMessage id="someMessage.foo" defaultMessage={"Some text, " + "and some more too. {someValue}"} description="A test message for babel." values={{
|
||||
someValue: "A value."
|
||||
}} />;
|
||||
|
||||
var Foo = React.createClass({
|
||||
render: function () {
|
||||
return _ref;
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,7 @@
|
||||
{
|
||||
|
||||
"plugins": [
|
||||
["transform-react-constant-elements", {"allowMutablePropsOnTags": ["FormattedMessage"]}],
|
||||
"syntax-jsx"
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
var Foo = React.createClass({
|
||||
render: function () {
|
||||
return (
|
||||
<FormattedMessage
|
||||
id="someMessage.foo"
|
||||
defaultMessage={
|
||||
"Some text, " +
|
||||
"and some more too. {someValue}"
|
||||
}
|
||||
description="A test message for babel."
|
||||
values={{
|
||||
someValue: "A value."
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
var _ref = <FormattedMessage id="someMessage.foo" defaultMessage={"Some text, " + "and some more too. {someValue}"} description="A test message for babel." values={{
|
||||
someValue: "A value."
|
||||
}} />;
|
||||
|
||||
var Foo = React.createClass({
|
||||
render: function () {
|
||||
return _ref;
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,7 @@
|
||||
{
|
||||
|
||||
"plugins": [
|
||||
["transform-react-constant-elements", {"allowMutablePropsOnTags": ["FormattedMessage"]}],
|
||||
"syntax-jsx"
|
||||
]
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user