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() {
|
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 scopes = this.scopes;
|
||||||
|
|
||||||
let scope = scopes.pop();
|
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
|
// 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;
|
if (attachTo.getFunctionParent() === this.path.getFunctionParent()) return;
|
||||||
|
|
||||||
|
// generate declaration and insert it to our point
|
||||||
let uid = attachTo.scope.generateUidIdentifier("ref");
|
let uid = attachTo.scope.generateUidIdentifier("ref");
|
||||||
|
|
||||||
attachTo.insertBefore([
|
attachTo.insertBefore([
|
||||||
t.variableDeclaration("var", [
|
t.variableDeclaration("var", [
|
||||||
t.variableDeclarator(uid, this.path.node)
|
t.variableDeclarator(uid, this.path.node)
|
||||||
@ -121,7 +151,6 @@ export default class PathHoister {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
let parent = this.path.parentPath;
|
let parent = this.path.parentPath;
|
||||||
|
|
||||||
if (parent.isJSXElement() && this.path.container === parent.node.children) {
|
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
|
// turning the `span` in `<div><span /></div>` to an expression so we need to wrap it with
|
||||||
// an expression container
|
// an expression container
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user