Fix cases for the optimize_server_react
transform (#59390)
Closes #59310, see attached test cases. Closes NEXT-1830
This commit is contained in:
parent
d5291fad49
commit
b141f3b278
7 changed files with 147 additions and 61 deletions
|
@ -129,76 +129,67 @@ impl Fold for OptimizeServerReact {
|
|||
}
|
||||
}
|
||||
|
||||
expr
|
||||
expr.fold_children_with(self)
|
||||
}
|
||||
|
||||
// const [state, setState] = useState(x);
|
||||
// const [state, setState] = React.useState(x);
|
||||
fn fold_var_declarators(&mut self, d: Vec<VarDeclarator>) -> Vec<VarDeclarator> {
|
||||
fn fold_var_declarator(&mut self, decl: VarDeclarator) -> VarDeclarator {
|
||||
if !self.optimize_use_state {
|
||||
return d;
|
||||
return decl;
|
||||
}
|
||||
|
||||
let mut new_d = vec![];
|
||||
for decl in d {
|
||||
if let Pat::Array(array_pat) = &decl.name {
|
||||
if array_pat.elems.len() == 2 {
|
||||
if let (Some(array_pat_1), Some(array_pat_2)) =
|
||||
(&array_pat.elems[0], &array_pat.elems[1])
|
||||
{
|
||||
if let Some(box Expr::Call(call)) = &decl.init {
|
||||
if let Callee::Expr(box Expr::Ident(f)) = &call.callee {
|
||||
if let Some(use_state_ident) = &self.use_state_ident {
|
||||
if &f.to_id() == use_state_ident && call.args.len() == 1 {
|
||||
// We do the optimization only if the arg is a literal or a
|
||||
// type that we can
|
||||
// be sure is not a function (e.g. {} or [] lit).
|
||||
// This is because useState allows a function as the
|
||||
// initialiser.
|
||||
match &call.args[0].expr {
|
||||
box Expr::Lit(_)
|
||||
| box Expr::Object(_)
|
||||
| box Expr::Array(_) => {
|
||||
// const state = x, setState = () => {};
|
||||
new_d.push(VarDeclarator {
|
||||
definite: false,
|
||||
name: array_pat_1.clone(),
|
||||
init: Some(call.args[0].expr.clone()),
|
||||
span: DUMMY_SP,
|
||||
});
|
||||
new_d.push(VarDeclarator {
|
||||
definite: false,
|
||||
name: array_pat_2.clone(),
|
||||
init: Some(Box::new(Expr::Arrow(ArrowExpr {
|
||||
body: Box::new(BlockStmtOrExpr::Expr(
|
||||
Box::new(Expr::Lit(Lit::Null(Null {
|
||||
span: DUMMY_SP,
|
||||
}))),
|
||||
)),
|
||||
params: vec![],
|
||||
is_async: false,
|
||||
is_generator: false,
|
||||
span: DUMMY_SP,
|
||||
type_params: None,
|
||||
return_type: None,
|
||||
}))),
|
||||
span: DUMMY_SP,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
if let Pat::Array(array_pat) = &decl.name {
|
||||
if array_pat.elems.len() == 2 {
|
||||
if let Some(box Expr::Call(call)) = &decl.init {
|
||||
if let Callee::Expr(box Expr::Ident(f)) = &call.callee {
|
||||
if let Some(use_state_ident) = &self.use_state_ident {
|
||||
if &f.to_id() == use_state_ident && call.args.len() == 1 {
|
||||
// We do the optimization only if the arg is a literal or a
|
||||
// type that we can
|
||||
// be sure is not a function (e.g. {} or [] lit).
|
||||
// This is because useState allows a function as the
|
||||
// initialiser.
|
||||
match &call.args[0].expr {
|
||||
box Expr::Lit(_) | box Expr::Object(_) | box Expr::Array(_) => {
|
||||
// const [state, setState] = [x, () => {}];
|
||||
return VarDeclarator {
|
||||
definite: false,
|
||||
name: decl.name.clone(),
|
||||
init: Some(Box::new(Expr::Array(ArrayLit {
|
||||
elems: vec![
|
||||
Some(call.args[0].expr.clone().into()),
|
||||
Some(
|
||||
Expr::Arrow(ArrowExpr {
|
||||
span: DUMMY_SP,
|
||||
body: Box::new(BlockStmtOrExpr::Expr(
|
||||
Box::new(Expr::Lit(Lit::Null(
|
||||
Null { span: DUMMY_SP },
|
||||
))),
|
||||
)),
|
||||
is_async: false,
|
||||
is_generator: false,
|
||||
params: vec![],
|
||||
return_type: None,
|
||||
type_params: None,
|
||||
})
|
||||
.into(),
|
||||
),
|
||||
],
|
||||
span: DUMMY_SP,
|
||||
}))),
|
||||
span: DUMMY_SP,
|
||||
};
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new_d.push(decl.fold_with(self));
|
||||
}
|
||||
|
||||
new_d
|
||||
decl.fold_children_with(self)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,8 +7,14 @@ import { FilterItem } from './item';
|
|||
export default function FilterItemDropdown({ list }) {
|
||||
const pathname = usePathname();
|
||||
const searchParams = useSearchParams();
|
||||
const active = '', setActive = ()=>null;
|
||||
const openSelect = false, setOpenSelect = ()=>null;
|
||||
const [active, setActive] = [
|
||||
'',
|
||||
()=>null
|
||||
];
|
||||
const [openSelect, setOpenSelect] = [
|
||||
false,
|
||||
()=>null
|
||||
];
|
||||
const ref = useRef(null);
|
||||
null;
|
||||
null;
|
||||
|
|
|
@ -1,13 +1,19 @@
|
|||
import { useState } from 'react';
|
||||
export default function App({ x }) {
|
||||
const state = 0, setState = ()=>null;
|
||||
const [state, setState] = [
|
||||
0,
|
||||
()=>null
|
||||
];
|
||||
const [state2, setState2] = useState(()=>0);
|
||||
const [state3, setState3] = useState(x);
|
||||
const s = useState(0);
|
||||
const [state4] = useState(0);
|
||||
const { a } = {
|
||||
a: 0
|
||||
}, setState5 = ()=>null;
|
||||
const [{ a }, setState5] = [
|
||||
{
|
||||
a: 0
|
||||
},
|
||||
()=>null
|
||||
];
|
||||
return <div>
|
||||
|
||||
<h1>Hello World</h1>
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
import { useState, useEffect, useLayoutEffect } from 'react'
|
||||
import React from 'react'
|
||||
|
||||
const Component = ({ children, fallback }) => {
|
||||
const [mounted, setMounted] = useState(false)
|
||||
useEffect(() => setMounted(true), [])
|
||||
if (!mounted) {
|
||||
return fallback ?? /* @__PURE__ */ jsx(Fragment, {})
|
||||
}
|
||||
return children
|
||||
}
|
||||
export { Component }
|
|
@ -0,0 +1,14 @@
|
|||
import { useState, useEffect, useLayoutEffect } from 'react';
|
||||
import React from 'react';
|
||||
const Component = ({ children, fallback })=>{
|
||||
const [mounted, setMounted] = [
|
||||
false,
|
||||
()=>null
|
||||
];
|
||||
null;
|
||||
if (!mounted) {
|
||||
return fallback ?? /* @__PURE__ */ jsx(Fragment, {});
|
||||
}
|
||||
return children;
|
||||
};
|
||||
export { Component };
|
|
@ -0,0 +1,31 @@
|
|||
import { useEffect, useLayoutEffect, useMemo } from 'react'
|
||||
import * as React from 'react'
|
||||
|
||||
export default function App() {
|
||||
useEffect(() => {
|
||||
console.log('Hello World')
|
||||
}, [])
|
||||
|
||||
useLayoutEffect(() => {
|
||||
function foo() {}
|
||||
return () => {}
|
||||
}, [1, 2, App])
|
||||
|
||||
useLayoutEffect(() => {}, [runSideEffect()])
|
||||
useEffect(() => {}, [1, runSideEffect(), 2])
|
||||
useEffect(() => {}, getArray())
|
||||
|
||||
const a = useMemo(() => {
|
||||
return 1
|
||||
}, [])
|
||||
|
||||
React.useEffect(() => {
|
||||
console.log('Hello World')
|
||||
})
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Hello World</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
import { useEffect, useLayoutEffect, useMemo } from 'react';
|
||||
import * as React from 'react';
|
||||
export default function App() {
|
||||
null;
|
||||
null;
|
||||
useLayoutEffect(()=>{}, [
|
||||
runSideEffect()
|
||||
]);
|
||||
useEffect(()=>{}, [
|
||||
1,
|
||||
runSideEffect(),
|
||||
2
|
||||
]);
|
||||
useEffect(()=>{}, getArray());
|
||||
const a = useMemo(()=>{
|
||||
return 1;
|
||||
}, []);
|
||||
React.useEffect(()=>{
|
||||
console.log('Hello World');
|
||||
});
|
||||
return <div>
|
||||
|
||||
<h1>Hello World</h1>
|
||||
|
||||
</div>;
|
||||
}
|
Loading…
Reference in a new issue