React的渲染"0"问题及源码分析

简介: React的渲染"0"问题及源码分析

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


image.png


预期是:状态为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。不会渲染nullundefinedboolean


源码疑惑


既然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(
  target: 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: Avoid adding a text separator in common cases.
  target.push(stringToChunk(encodeHTMLTextNode(text)), textSeparator);
}

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





相关文章
|
7月前
|
前端开发 JavaScript
React如何进行条件渲染
React如何进行条件渲染
47 0
|
2月前
|
前端开发 JavaScript
React学习之——条件渲染
【10月更文挑战第16天】React 中没有像Vue中v-if这种指令。React 中的条件渲染和 JavaScript 中的一样,使用 JavaScript 运算符 if 或者条件运算符去创建元素来表现当前的状态,然后让 React 根据它们来更新 UI。
|
2月前
|
前端开发 JavaScript 容器
React 元素渲染
10月更文挑战第7天
34 1
|
2月前
|
监控 前端开发 UED
在 React 18 中利用并发渲染提高应用性能
【10月更文挑战第12天】利用并发渲染需要综合考虑应用的特点和需求,合理运用相关特性和策略,不断进行优化和调整,以达到最佳的性能提升效果。同时,要密切关注 React 的发展和更新,以便及时利用新的技术和方法来进一步优化应用性能。你还可以结合具体的项目实践来深入理解和掌握这些方法,让应用在 React 18 的并发渲染机制下发挥出更好的性能优势。
126 59
|
2月前
|
JavaScript 前端开发 算法
前端优化之超大数组更新:深入分析Vue/React/Svelte的更新渲染策略
本文对比了 Vue、React 和 Svelte 在数组渲染方面的实现方式和优缺点,探讨了它们与直接操作 DOM 的差异及 Web Components 的实现方式。Vue 通过响应式系统自动管理数据变化,React 利用虚拟 DOM 和 `diffing` 算法优化更新,Svelte 通过编译时优化提升性能。文章还介绍了数组更新的优化策略,如使用 `key`、分片渲染、虚拟滚动等,帮助开发者在处理大型数组时提升性能。总结指出,选择合适的框架应根据项目复杂度和性能需求来决定。
|
2月前
|
前端开发 JavaScript 算法
React 渲染优化策略
【10月更文挑战第6天】React 是一个高效的 JavaScript 库,用于构建用户界面。本文从基础概念出发,深入探讨了 React 渲染优化的常见问题及解决方法,包括不必要的渲染、大量子组件的渲染、高频事件处理和大量列表渲染等问题,并提供了代码示例,帮助开发者提升应用性能。
61 6
|
2月前
|
前端开发 JavaScript
React 条件渲染
10月更文挑战第9天
32 0
|
4月前
|
资源调度 前端开发 API
React Suspense与Concurrent Mode:异步渲染的未来
React的Suspense与Concurrent Mode是16.8版后引入的功能,旨在改善用户体验与性能。Suspense组件作为异步边界,允许子组件在数据加载完成前显示占位符,结合React.lazy实现懒加载,优化资源调度。Concurrent Mode则通过并发渲染与智能调度提升应用响应性,支持时间分片和优先级调度,确保即使处理复杂任务时UI仍流畅。二者结合使用,能显著提高应用效率与交互体验,尤其适用于数据驱动的应用场景。
86 20
|
4月前
|
前端开发
React 如何使用条件渲染
【8月更文挑战第17天】React 如何使用条件渲染
47 3
|
4月前
|
前端开发 JavaScript 中间件