这个例子里的 component 总是重新挂载

2022-11-08 09:41:01 +08:00
 sillydaddy
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++不会导致重新挂载。

什么决定了重新挂载呢?这里的父子组件结构,并没有变化啊。

2238 次点击
所在节点    React
22 条回复
withoutxx
2022-11-08 09:45:51 +08:00
somevar 变动就会重新 render Main 组件生成一个新的 SubComponent 组件,
用 useMemo 包裹一下 SubComponent 组件或者直接放到外面去
luvsic
2022-11-08 09:46:57 +08:00
因为 somevar++ 会重新执行 Main 函数
sillydaddy
2022-11-08 09:58:39 +08:00
@withoutxx
是重新生成 SubComponent 啊,React 的 render 就是这个流程啊,但是为啥会 unmount 呢?

按我的理解,再次渲染时,父子组件的结构并没有变,就不应该 unmount 啊。

组件的结构⬇️
```
<Main>
...
<SubComponent />
</Main>
```
sillydaddy
2022-11-08 09:59:55 +08:00
@luvsic 但为什么会 unmount 呢?难道所有的 re-render 都会 unmount 所有子组件?这不可能吧。
huai
2022-11-08 10:08:47 +08:00
定义 subComponent 可以挪到外部去。或者加上 useMemo

你的问题应该看下 diff 流程 ,能找到答案
maichael
2022-11-08 10:14:32 +08:00
因为 SubComponent 被重新赋值了,你以为的“SubComponent”其实已经是一个新的“SubComponent”了
sillydaddy
2022-11-08 10:29:05 +08:00
@maichael
原来如此!!一不注意又进了 lambda 的「陷阱」。。怪不得楼上都在说移到外面。
sankemao
2022-11-08 10:29:12 +08:00
组件不要写在 hook 函数中
otakustay
2022-11-08 10:30:28 +08:00
type 或 key 任意一个变化就会重新挂载,你的例子里 type 一直在变(引用不同)
johnkiller
2022-11-08 10:53:38 +08:00
楼上也是正解
sillydaddy
2022-11-08 11:36:52 +08:00
@otakustay
对的,应该是因为 type 一直在变
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}
</>
);
}
```
MrYELiex
2022-11-08 13:24:46 +08:00
每次都重新声明函数当然会重新渲染 react 前是 js 基础
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>
mufeng
2022-11-08 13:33:09 +08:00
@mufeng 每次 somevar 变化 `() => setSomevar(somevar + 1)` 都是一个新函数
sillydaddy
2022-11-08 13:44:20 +08:00
@mufeng
嗯,看来回调的地方也要注意:lambda 表达式每次都是生成一个新的实例。
nulIptr
2022-11-08 17:03:57 +08:00
@mufeng 这么改可以但是没必要。props 变化不会导致 unmount
性能优化很重要的一条是:不要过早优化,这也是官方文档的意思,如果不确定需不需要加 useMemo/useCallback ,那就不加,pref 有问题再解决
maclanelf134
2022-11-08 17:11:44 +08:00
react state 变化就是会 触发页面重新渲染的,不想子组件跟着变,用 memo 函数包一下
siwadiya
2022-11-09 11:04:04 +08:00
@me221 这种情况下要怎么往 SubComponent 传参呢,并且 useMemo 内怎么接收参数。
siwadiya
2022-11-09 11:07:45 +08:00
仔细一想,这种情况好像也没有传参的必要了😂

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/893491

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX