const Main = (props)=>{ var [somevar, setSomevar] = useState<number>(0); const SubCompOnent= (props)=>{ var [count, setCount] = useState<number>(0); useEffect(()=>{ console.log("SubComponent mounted!"); return ()=>{console.log("SubComponent dis mounted");}; }, [props]); return (<div> <div>count:{count}</div> <button OnClick={()=>setCount(count+1)}>count++</button> </div>); } return (<div> <div>somevar:{somevar}</div> <button OnClick={()=>setSomevar(somevar+1)}>somevar++</button> <SubComponent> </SubComponent> </div>); }
每次点击 somevar++,SubComponent 都会重新 mount 和 unmount ,而且 count 也会清零。 点击 count++不会导致重新挂载。
什么决定了重新挂载呢?这里的父子组件结构,并没有变化啊。
![]() | 1 withoutxx 2022-11-08 09:45:51 +08:00 somevar 变动就会重新 render Main 组件生成一个新的 SubComponent 组件, 用 useMemo 包裹一下 SubComponent 组件或者直接放到外面去 |
![]() | 2 luvsic 2022-11-08 09:46:57 +08:00 因为 somevar++ 会重新执行 Main 函数 |
![]() | 3 sillydaddy OP @withoutxx 是重新生成 SubComponent 啊,React 的 render 就是这个流程啊,但是为啥会 unmount 呢? 按我的理解,再次渲染时,父子组件的结构并没有变,就不应该 unmount 啊。 组件的结构 ``` <Main> ... <SubComponent /> </Main> ``` |
![]() | 4 sillydaddy OP @luvsic 但为什么会 unmount 呢?难道所有的 re-render 都会 unmount 所有子组件?这不可能吧。 |
![]() | 5 huai 2022-11-08 10:08:47 +08:00 定义 subComponent 可以挪到外部去。或者加上 useMemo 你的问题应该看下 diff 流程 ,能找到答案 |
![]() | 6 maichael 2022-11-08 10:14:32 +08:00 ![]() 因为 SubComponent 被重新赋值了,你以为的“SubComponent”其实已经是一个新的“SubComponent”了 |
![]() | 7 sillydaddy OP @maichael 原来如此!!一不注意又进了 lambda 的「陷阱」。。怪不得楼上都在说移到外面。 |
![]() | 8 sankemao 2022-11-08 10:29:12 +08:00 组件不要写在 hook 函数中 |
![]() | 9 otakustay 2022-11-08 10:30:28 +08:00 ![]() type 或 key 任意一个变化就会重新挂载,你的例子里 type 一直在变(引用不同) |
10 johnkiller 2022-11-08 10:53:38 +08:00 via iPhone 楼上也是正解 |
![]() | 11 sillydaddy OP @otakustay 对的,应该是因为 type 一直在变 |
12 me221 2022-11-08 11:53:22 +08:00 补充 useMemo 用法: ``` const Main = () => { const [somevar, setSomevar] = useState<number>(0); const [count, setCount] = useState<number>(0); const SubCompOnent= useMemo(() => { return ( <div> <div>count:{count}</div> <button OnClick={() => setCount(count + 1)}>count++</button> </div> ); }, [count]) return ( <> <div>somevar:{somevar}</div> <button OnClick={() => setSomevar(somevar + 1)}>somevar++</button> {SubComponent} </> ); } ``` |
![]() | 13 MrYELiex 2022-11-08 13:24:46 +08:00 每次都重新声明函数当然会重新渲染 react 前是 js 基础 |
![]() | 14 mufeng 2022-11-08 13:31:26 +08:00 这么写一定会 rerender ,官方的例子都是这么写,实际上就是误导: <button OnClick={()=>setSomevar(somevar+1)}>somevar++</button> 改成 const func1 = useCallback(() => { setSomevar(somevar + 1) }, [somevar]) <button OnClick={func1}>somevar++</button> |
![]() | 16 sillydaddy OP @mufeng 嗯,看来回调的地方也要注意:lambda 表达式每次都是生成一个新的实例。 |
17 nulIptr 2022-11-08 17:03:57 +08:00 @mufeng 这么改可以但是没必要。props 变化不会导致 unmount 性能优化很重要的一条是:不要过早优化,这也是官方文档的意思,如果不确定需不需要加 useMemo/useCallback ,那就不加,pref 有问题再解决 |
18 maclanelf134 2022-11-08 17:11:44 +08:00 react state 变化就是会 触发页面重新渲染的,不想子组件跟着变,用 memo 函数包一下 |
20 siwadiya 2022-11-09 11:07:45 +08:00 仔细一想,这种情况好像也没有传参的必要了 |
![]() | 21 theohateonion 2022-11-09 12:32:04 +08:00 @sillydaddy 或者换一种理解,每次函数都会重新执行。所以 subComponent 每次 state 变化都会是新的。 |
![]() | 22 ragnaroks 2022-11-10 10:20:42 +08:00 @sillydaddy 不是 lambda 表达式每次都是生成一个新的实例,而是你的例子中,"somevar" 是一个被引用的变量。 <button OnClick={()=>setSomevar(somevar + 1)}>somevar++</button> 可能会重新渲染。 但 <button OnClick={()=>setSomevar(451)}>somevar++</button> 不会。 |