首页 > 编程语言 >React的零渲染问题及源码分析

React的零渲染问题及源码分析

时间:2022-10-07 23:00:48浏览次数:63  
标签:obj 渲染 React 源码 && null

开门见山,先来看一张bug图(状态下面有个00)。

React的零渲染问题及源码分析_查看源码

预期是:状态为0时,2个组件不做渲染。 现状:状态为0时,2个组件不做渲染,但是渲染出了00。

  • 零渲染 bug 代码
  • 如何修复零渲染问题
  • 初窥源码
  • 源码疑惑
  • 原因总结
  • 源码实锤

零渲染 bug 代码

什么是React的零渲染问题?看下下面这段代码,我们会经常这样写:

// bug代码 0
{obj?.count && <span>{obj?.count}</span>}

假如obj?.count为0,渲染结果为0。这里不就1个0么,上面为什么是00呢。

// bug代码 00 
{obj?.countFoo && <span>{obj?.countFoo}</span>}
{obj?.countBar && <span>{obj?.countBar}</span>}

当obj?.countFoo和obj?.countBar都为0时,渲染结果为00。

如何修复零渲染问题

{!!obj?.count && <span>{obj?.count}</span>}

或者

{obj?.count ? <span>{obj?.count}</span> : null}

或者

{Boolean(obj?.count) && <span>{obj?.count}</span>}

初窥源码

原因(点击类型查看源码):React组件会渲染​​string,number​​​。不会渲染​​null​​​,​​undefined​​​,​​boolean​​。

源码疑惑

既然boolean会被处理为null,那为什么​​true && <FooComponent />​​可以正常渲染呢?先说结论,因为进行了&&运算,React最终渲染的是jsx与计算后的结果。

const type = typeof children;
if (type === 'undefined' || type === 'boolean') {
// All of the above are perceived as null.
children = null;
}

也就是说 ​​此处​​的children,是jsx计算后的结果。

举例如下:

// 可渲染值
1 && <FooComponent // => jsx计算结果为<FooComponent />,因此渲染<FooComponent/>
"a string" && <FooComponent // => jsx计算结果为<FooComponent />,因此渲染<FooComponent />
0 && <FooComponent // => jsx计算结果为0,Renders '0'
true && <FooComponent // => jsx计算结果为<FooComponent />,因此渲染<FooComponent />

// 不可渲染值
false && <FooComponent // => jsx计算结果为false,因此什么都不渲染
null && <FooComponent // => jsx计算结果为null,因此什么都不渲染
undefined && <FooComponent // => jsx计算结果为undefined,因此什么都不渲染

原因总结

其实,根本不是React渲染什么的问题,而是&&操作符后返回值的问题。所以,最根本是因为

  • React渲染string,number,正常组件
  • React不渲染undefined,boolean,null
{"1"} // 渲染为"1"
{0} // 渲染为0
{<FooComponent} // 假设为正常组件,渲染为<FooComponent />

{undefined} // 不渲染
{true} // 不渲染
{false} // 不渲染

{null} // 不渲染

源码实锤

const type = typeof children;

// React不渲染undefined,boolean
if (type === 'undefined' || type === 'boolean') {
// All of the above are perceived as null.
children = null;
}

let invokeCallback = false;

if (children === null) {
invokeCallback = true;
} else {
switch (type) {
case 'string':
case 'number':
// React渲染string,number
invokeCallback = true;
break;
case 'object':
// React渲染正常组件
switch ((children: any).$$typeof) {
case REACT_ELEMENT_TYPE:
case REACT_PORTAL_TYPE:
invokeCallback = true;
}
}
}

原始值为null,和undefined以及boolean最终被处理为null,​​React不渲染null的源码实锤呢​​?

render(
child: ReactNode | null,
context: Object,
parentNamespace: string,
): string {
if (typeof child === 'string' || typeof child === 'number') {
const text = '' + child;
if (text === '') {
return '';
}
this.previousWasTextNode = true;
return escapeTextForBrowser(text);
} else {
let nextChild;
({child: nextChild, context} = resolve(child, context, this.threadID));
// React不渲染null
if (nextChild === null || nextChild === false) {
return '';
}

对于html标签渲染空字符串而言,空字符串会被渲染,例如​​<div>""</div>​​​会被渲染为​​<div>""</div>​​​但对于react而言,完整流程为​​{null} =>{""} => nothing​​例如下面这样:

<div>{''}</div> // => <div></div>
<div>{' '}</div> // => <div> </div>

那么,​​React是如何处理空字符串的呢?​

export function pushTextInstance(Array<Chunk | PrecomputedChunk>,
text: string,
responseState: ResponseState,
): void {
if (text === '') {
// Empty text doesn't have a DOM node representation and the hydration is aware of this.
// 这句注释的意思是:空文本节点没有DOM节点表示,它属于textNode
return;
}
// TODO:
target.push(stringToChunk(encodeHTMLTextNode(text)), textSeparator);
}

从源码我们可以看到,对于空文本节点,React会直接return出去,不会去生成文本实例(TextInstance)。

标签:obj,渲染,React,源码,&&,null
From: https://blog.51cto.com/u_15725382/5735215

相关文章