Fix React constant elements transform from hoisting elements to positions where their referenced bindings haven't been evaluated yet (#3596)
This commit is contained in:
parent
8d14f9f4d0
commit
3b4b3656a8
@ -0,0 +1,14 @@
|
||||
const AppItem = () => {
|
||||
return <div>child</div>;
|
||||
};
|
||||
|
||||
export default class App extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<p>Parent</p>
|
||||
<AppItem />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
var _ref = <div>child</div>;
|
||||
|
||||
const AppItem = () => {
|
||||
return _ref;
|
||||
};
|
||||
|
||||
var _ref2 = <div>
|
||||
<p>Parent</p>
|
||||
<AppItem />
|
||||
</div>;
|
||||
|
||||
export default class App extends React.Component {
|
||||
render() {
|
||||
return _ref2;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
(function () {
|
||||
class App extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<p>Parent</p>
|
||||
<AppItem />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const AppItem = () => {
|
||||
return <div>child</div>;
|
||||
};
|
||||
});
|
||||
@ -0,0 +1,18 @@
|
||||
var _ref = <p>Parent</p>;
|
||||
|
||||
var _ref2 = <div>child</div>;
|
||||
|
||||
(function () {
|
||||
class App extends React.Component {
|
||||
render() {
|
||||
return <div>
|
||||
{_ref}
|
||||
<AppItem />
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
const AppItem = () => {
|
||||
return _ref2;
|
||||
};
|
||||
});
|
||||
@ -0,0 +1,16 @@
|
||||
(function () {
|
||||
const AppItem = () => {
|
||||
return <div>child</div>;
|
||||
};
|
||||
|
||||
class App extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<p>Parent</p>
|
||||
<AppItem />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,20 @@
|
||||
var _ref = <div>child</div>;
|
||||
|
||||
var _ref3 = <p>Parent</p>;
|
||||
|
||||
(function () {
|
||||
const AppItem = () => {
|
||||
return _ref;
|
||||
};
|
||||
|
||||
var _ref2 = <div>
|
||||
{_ref3}
|
||||
<AppItem />
|
||||
</div>;
|
||||
|
||||
class App extends React.Component {
|
||||
render() {
|
||||
return _ref2;
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,14 @@
|
||||
export default class App extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<p>Parent</p>
|
||||
<AppItem />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const AppItem = () => {
|
||||
return <div>child</div>;
|
||||
};
|
||||
@ -0,0 +1,16 @@
|
||||
var _ref = <p>Parent</p>;
|
||||
|
||||
export default class App extends React.Component {
|
||||
render() {
|
||||
return <div>
|
||||
{_ref}
|
||||
<AppItem />
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
var _ref2 = <div>child</div>;
|
||||
|
||||
const AppItem = () => {
|
||||
return _ref2;
|
||||
};
|
||||
@ -61,6 +61,36 @@ export default class PathHoister {
|
||||
}
|
||||
|
||||
getAttachmentPath() {
|
||||
let path = this._getAttachmentPath();
|
||||
if (!path) return;
|
||||
|
||||
let targetScope = path.scope;
|
||||
|
||||
// don't allow paths that have their own lexical environments to pollute
|
||||
if (targetScope.path === path) {
|
||||
targetScope = path.scope.parent;
|
||||
}
|
||||
|
||||
// avoid hoisting to a scope that contains bindings that are executed after our attachment path
|
||||
if (targetScope.path.isProgram() || targetScope.path.isFunction()) {
|
||||
for (let name in this.bindings) {
|
||||
// check binding is a direct child of this paths scope
|
||||
if (!targetScope.hasOwnBinding(name)) continue;
|
||||
|
||||
let binding = this.bindings[name];
|
||||
|
||||
// allow parameter references
|
||||
if (binding.kind === "param") continue;
|
||||
|
||||
// if this binding appears after our attachment point then don't hoist it
|
||||
if (binding.path.getStatementParent().key > path.key) return;
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
_getAttachmentPath() {
|
||||
let scopes = this.scopes;
|
||||
|
||||
let scope = scopes.pop();
|
||||
@ -112,8 +142,8 @@ export default class PathHoister {
|
||||
// don't bother hoisting to the same function as this will cause multiple branches to be evaluated more than once leading to a bad optimisation
|
||||
if (attachTo.getFunctionParent() === this.path.getFunctionParent()) return;
|
||||
|
||||
// generate declaration and insert it to our point
|
||||
let uid = attachTo.scope.generateUidIdentifier("ref");
|
||||
|
||||
attachTo.insertBefore([
|
||||
t.variableDeclaration("var", [
|
||||
t.variableDeclarator(uid, this.path.node)
|
||||
@ -121,7 +151,6 @@ export default class PathHoister {
|
||||
]);
|
||||
|
||||
let parent = this.path.parentPath;
|
||||
|
||||
if (parent.isJSXElement() && this.path.container === parent.node.children) {
|
||||
// turning the `span` in `<div><span /></div>` to an expression so we need to wrap it with
|
||||
// an expression container
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user