最近在读 React 官方文档,读到「在渲染树中保留状态」这块了。文档提到,有两个示例程序是不同的,但没讲为什么。果然网上有一波人跟我有一模一样的困惑 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
VisualStudioCode
V2EX    React

最近在读 React 官方文档,读到「在渲染树中保留状态」这块了。文档提到,有两个示例程序是不同的,但没讲为什么。果然网上有一波人跟我有一模一样的困惑

  •  
  •   VisualStudioCode 2023-11-04 16:53:39 +08:00 3309 次点击
    这是一个创建于 706 天前的主题,其中的信息可能已经有所发展或是发生改变。
    第 1 条附言    2023-11-05 12:32:02 +08:00

    个人总结

    「同一位置」之定义,不仅取决于组件所处位置之JSX层级

    还取决于组件所处层级中,与其他组件的先后顺序。

    在第一个示例程序中:

     return ( <div> {isPlayerA ? ( <Counter person="Taylor" /> ) : ( <Counter person="Sarah" /> )} <button OnClick={() => { setIsPlayerA(!isPlayerA); }}> Next player! </button> </div> ); 

    无论isPlayerA是true还是false,<div>下都只有两个元素,一个组件<Counter/>,一个<button>。

    而对于第二个示例程序:

     return ( <div> {isPlayerA && <Counter person="Taylor" /> } {!isPlayerA && <Counter person="Sarah" /> } <button OnClick={() => { setIsPlayerA(!isPlayerA); }}> Next player! </button> </div> ); 

    当isPlayerA为true时:

     <div> <Counter person="Taylor" /> {false} <button OnClick={() => { setIsPlayerA(!isPlayerA); }}> Next player! </button> </div> 

    当isPlayerA为false时:

     <div> {false} <Counter person="Sarah" /> <button OnClick={() => { setIsPlayerA(!isPlayerA); }}> Next player! </button> </div> 

    <Counter/>组件所处顺序不同,React就不会保留它的状态。

    第 2 条附言    2023-11-05 14:09:58 +08:00
    最后两个代码块的意思是等价的 JSX ,即:
    「当 isPlayerA 为 true 时:等价的 JSX 是……」
    「当 isPlayerA 为 false 时:等价的 JSX 是……」
    16 条回复    2023-11-05 12:38:58 +08:00
    huangzhiyia
        1
    huangzhiyia  
       2023-11-04 17:00:49 +08:00   1
    区别是这个地方

    {isPlayerA ? (
    <Counter person="Taylor" />
    ) : (
    <Counter person="Sarah" />
    )}

    这个状态会保留

    {isPlayerA &&
    <Counter person="Taylor" />
    }
    {!isPlayerA &&
    <Counter person="Sarah" />
    }
    这个状态不会保留
    VisualStudioCode
        2
    VisualStudioCode  
    OP
       2023-11-04 17:09:17 +08:00
    @zmaplex
    呃,我当然知道啊。但重点在于为什么啊……
    这个回答讲,好像是由于 entry 这个概念。https://stackoverflow.com/a/75885252/10553078
    thevita
        3
    thevita  
       2023-11-04 17:39:42 +08:00
    这样,你把 render tree 分别打出来看就明白了

    ```
    .....

    const r = (
    <div>
    {isPlayerA ? <Counter person="Taylor" /> : <Counter person="Sarah" />}
    <button
    OnClick={() => {
    setIsPlayerA(!isPlayerA);
    }}
    >
    Next player!
    </button>
    </div>
    );
    console.log(r);
    return r;

    ......

    ```


    https://codesandbox.io/s/shy-firefly-3xt4ck




    props:
    children: Array(2)
    0: {$$typeof: Symbol(react.element), key: null, ref: null, props: {…}, type: , …}
    1: {$$typeof: Symbol(react.element), type: 'button', key: null, ref: null, props: {…}, …}

    https://codesandbox.io/s/quizzical-artem-5j8xqk



    props:
    children: Array(2)
    0: {$$typeof: Symbol(react.element), key: null, ref: null, props: {…}, type: , …}
    1: false
    3: {$$typeof: Symbol(react.element), type: 'button', key: null, ref: null, props: {…}, …}



    render tree 是不一样的, 后面 diff 的时候 前一种 是没办法识别 Counter 这个 element 的变化的


    ps: 不写 react ,描述按我自己理解来的,但大概应该就是这个样子
    codehz
        4
    codehz  
       2023-11-04 17:45:19 +08:00 via iPhone
    你想哈既然 jsx 本质就是 js 语法糖,那如果直接用?:语法 react 怎么知道它是改了整个组件,还是单纯只改了 props 呢(组件相同,仅 props 不同,没有 key 的情况下如何区分?)
    而&&的写法,因为分属于数组的两个不同位置,react 可以直接根据位置来推断是不是同一个组件
    这与其说是 react 特意设计的特性,不如说是 jsx 语法转换的副作用(虽然 jsx 严格意义上也算是 react 的设计),不过好歹 react 给了一个文档说了
    vivipure
        5
    vivipure  
       2023-11-04 17:48:06 +08:00
    这东西太不符合直觉了,按理来说每个组件都应该是自己的状态的,这个切换还能保留状态。
    hua123s
        6
    hua123s  
       2023-11-04 18:02:08 +08:00 via Android
    就是因为 key 不一样,所以状态丢了。
    noe132
        7
    noe132  
       2023-11-04 18:51:44 +08:00
    第一种写法和
    <Counter person={isPlayerA ? "Taylor": "Sarah"} />
    是等价的。

    React 对于同组件同位置的节点会在重新渲染时会被认为是同一个实例。
    区分不同实例的方式是使用不同的 key 。
    lisongeee
        8
    lisongeee  
       2023-11-04 19:40:28 +08:00   3
    把 jsx 转换为 原生 js 就很直白了

    ![image]( https://github.com/gkd-kit/gkd/assets/38517192/d586fcd2-9539-4b94-87b0-3c6a8886297e)
    ![image]( https://github.com/gkd-kit/gkd/assets/38517192/74700d26-b9fc-4bc2-9b13-995d17db5639)

    你的第二个示例程序,返回的数组中当 isPlayerA 真时是 [Fc, null],假时为 [null, Fc]
    而第一个示例程序,返回的数组中当 isPlayerA 真时是 [Fc],假时为 [Fc],因切换后状态不会丢失
    RabbitDR
        9
    RabbitDR  
       2023-11-04 20:01:29 +08:00
    8 楼的图片很清晰了,前者通过三元算出来的在同一个位置,后者是真的两个不同的位置,这里的位置指 createElement 的入参。
    VisualStudioCode
        10
    VisualStudioCode  
    OP
       2023-11-04 20:56:25 +08:00
    @vivipure 「这东西太不符合直觉了」

    根据文档里讲的「快照机制」,应该也还好理解。
    https://react.dev/learn/state-as-a-snapshot
    VisualStudioCode
        11
    VisualStudioCode  
    OP
       2023-11-04 23:44:08 +08:00
    @lisongeee 谢谢讲解。

    请问这是怎么转换的?
    lisongeee
        12
    lisongeee  
       2023-11-04 23:56:55 +08:00
    @VisualStudioCode

    > 请问这是怎么转换的?

    https://babeljs.io/repl
    SingeeKing
        13
    SingeeKing  
    PRO
       2023-11-05 02:59:54 +08:00
    本质上就是 children 是一个数组,每个语句会被转换成 children 的一个成员

    使用 ?: 时是一个语句,也就对应一个成员
    使用 && 时是两个语句了,也就对应两个不同的成员了
    HaroldFinchNYC
        14
    HaroldFinchNYC  
       2023-11-05 09:03:14 +08:00
    VisualStudioCode
        15
    VisualStudioCode  
    OP
       2023-11-05 11:40:01 +08:00
    @vivipure
    好吧,我也只是在学习中,不知道这一段文档是否跟快照机制有关。
    VisualStudioCode
        16
    VisualStudioCode  
    OP
       2023-11-05 12:38:58 +08:00
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1216 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 24ms UTC 17:26 PVG 01:26 LAX 10:26 JFK 13:26
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86