Skip to content

Commit

Permalink
Fix cases for the optimize_server_react transform (#59390)
Browse files Browse the repository at this point in the history
Closes #59310, see attached test cases.

Closes NEXT-1830
  • Loading branch information
shuding committed Dec 8, 2023
1 parent d5291fa commit b141f3b
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 61 deletions.
101 changes: 46 additions & 55 deletions packages/next-swc/crates/core/src/optimize_server_react.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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>
Expand Down
Original file line number Diff line number Diff line change
@@ -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 }
Original file line number Diff line number Diff line change
@@ -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 };
Original file line number Diff line number Diff line change
@@ -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>
)
}
Original file line number Diff line number Diff line change
@@ -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>;
}

0 comments on commit b141f3b

Please sign in to comment.