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

2023-11-04 16:53:39 +08:00
 VisualStudioCode

第一个示例程序: https://codesandbox.io/s/ly5h9z?file=%2FApp.js

文档位置: https://react.dev/learn/preserving-and-resetting-state#resetting-state-at-the-same-position

第二个示例程序: https://codesandbox.io/s/cp7y7q?file=%2FApp.js

文档位置: https://react.dev/learn/preserving-and-resetting-state#option-1-rendering-a-component-in-different-positions

谷歌一搜,全是同样的困惑:

2800 次点击
所在节点    React
16 条回复
zmaplex
2023-11-04 17:00:49 +08:00
区别是这个地方

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

这个状态会保留

{isPlayerA &&
<Counter person="Taylor" />
}
{!isPlayerA &&
<Counter person="Sarah" />
}
这个状态不会保留
VisualStudioCode
2023-11-04 17:09:17 +08:00
@zmaplex
呃,我当然知道啊。但重点在于为什么啊……
这个回答讲,好像是由于 entry 这个概念。https://stackoverflow.com/a/75885252/10553078
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
2023-11-04 17:45:19 +08:00
你想哈既然 jsx 本质就是 js 语法糖,那如果直接用?:语法 react 怎么知道它是改了整个组件,还是单纯只改了 props 呢(组件相同,仅 props 不同,没有 key 的情况下如何区分?)
而&&的写法,因为分属于数组的两个不同位置,react 可以直接根据位置来推断是不是同一个组件
这与其说是 react 特意设计的特性,不如说是 jsx 语法转换的副作用(虽然 jsx 严格意义上也算是 react 的设计),不过好歹 react 给了一个文档说了
vivipure
2023-11-04 17:48:06 +08:00
这东西太不符合直觉了,按理来说每个组件都应该是自己的状态的,这个切换还能保留状态。
hua123s
2023-11-04 18:02:08 +08:00
就是因为 key 不一样,所以状态丢了。
noe132
2023-11-04 18:51:44 +08:00
第一种写法和
<Counter person={isPlayerA ? "Taylor": "Sarah"} />
是等价的。

React 对于同组件同位置的节点会在重新渲染时会被认为是同一个实例。
区分不同实例的方式是使用不同的 key 。
lisongeee
2023-11-04 19:40:28 +08:00
把 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
2023-11-04 20:01:29 +08:00
8 楼的图片很清晰了,前者通过三元算出来的在同一个位置,后者是真的两个不同的位置,这里的位置指 createElement 的入参。
VisualStudioCode
2023-11-04 20:56:25 +08:00
@vivipure 「这东西太不符合直觉了」

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

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

> 请问这是怎么转换的?

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

使用 ?: 时是一个语句,也就对应一个成员
使用 && 时是两个语句了,也就对应两个不同的成员了
HaroldFinchNYC
2023-11-05 09:03:14 +08:00
VisualStudioCode
2023-11-05 11:40:01 +08:00
@vivipure
好吧,我也只是在学习中,不知道这一段文档是否跟快照机制有关。
VisualStudioCode
2023-11-05 12:38:58 +08:00

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

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

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

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

© 2021 V2EX