乐趣区

关于前端:React的零渲染问题及源码分析

单刀直入,先来看一张 bug 图(状态上面有个 00)。

预期是:状态为 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 的源码实锤呢?
源码地址:https://github.com/facebook/r…

  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>
残缺流程为{null} =>{""} => nothing

退出移动版