V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
sillydaddy
V2EX  ›  React

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

  •  
  •   sillydaddy · 2022-11-08 09:41:01 +08:00 · 2199 次点击
    这是一个创建于 506 天前的主题,其中的信息可能已经有所发展或是发生改变。
    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++不会导致重新挂载。

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

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

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

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

    你的问题应该看下 diff 流程 ,能找到答案
    maichael
        6
    maichael  
       2022-11-08 10:14:32 +08:00   ❤️ 2
    因为 SubComponent 被重新赋值了,你以为的“SubComponent”其实已经是一个新的“SubComponent”了
    sillydaddy
        7
    sillydaddy  
    OP
       2022-11-08 10:29:05 +08:00
    @maichael
    原来如此!!一不注意又进了 lambda 的「陷阱」。。怪不得楼上都在说移到外面。
    sankemao
        8
    sankemao  
       2022-11-08 10:29:12 +08:00
    组件不要写在 hook 函数中
    otakustay
        9
    otakustay  
       2022-11-08 10:30:28 +08:00   ❤️ 1
    type 或 key 任意一个变化就会重新挂载,你的例子里 type 一直在变(引用不同)
    johnkiller
        10
    johnkiller  
       2022-11-08 10:53:38 +08:00 via iPhone
    楼上也是正解
    sillydaddy
        11
    sillydaddy  
    OP
       2022-11-08 11:36:52 +08:00
    @otakustay
    对的,应该是因为 type 一直在变
    me221
        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}
    </>
    );
    }
    ```
    MrYELiex
        13
    MrYELiex  
       2022-11-08 13:24:46 +08:00
    每次都重新声明函数当然会重新渲染 react 前是 js 基础
    mufeng
        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>
    mufeng
        15
    mufeng  
       2022-11-08 13:33:09 +08:00
    @mufeng 每次 somevar 变化 `() => setSomevar(somevar + 1)` 都是一个新函数
    sillydaddy
        16
    sillydaddy  
    OP
       2022-11-08 13:44:20 +08:00
    @mufeng
    嗯,看来回调的地方也要注意:lambda 表达式每次都是生成一个新的实例。
    nulIptr
        17
    nulIptr  
       2022-11-08 17:03:57 +08:00
    @mufeng 这么改可以但是没必要。props 变化不会导致 unmount
    性能优化很重要的一条是:不要过早优化,这也是官方文档的意思,如果不确定需不需要加 useMemo/useCallback ,那就不加,pref 有问题再解决
    maclanelf134
        18
    maclanelf134  
       2022-11-08 17:11:44 +08:00
    react state 变化就是会 触发页面重新渲染的,不想子组件跟着变,用 memo 函数包一下
    siwadiya
        19
    siwadiya  
       2022-11-09 11:04:04 +08:00
    @me221 这种情况下要怎么往 SubComponent 传参呢,并且 useMemo 内怎么接收参数。
    siwadiya
        20
    siwadiya  
       2022-11-09 11:07:45 +08:00
    仔细一想,这种情况好像也没有传参的必要了😂
    theohateonion
        21
    theohateonion  
       2022-11-09 12:32:04 +08:00
    @sillydaddy 或者换一种理解,每次函数都会重新执行。所以 subComponent 每次 state 变化都会是新的。
    ragnaroks
        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> 不会。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1240 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 17:58 · PVG 01:58 · LAX 10:58 · JFK 13:58
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.