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 2
因为 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 1
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> 不会。 |