• wordpress CMS主题:ssmay主题 wordpress CMS主题:ssmay主题
  • 首页 > 前端开发 > 精读《React 八种条件渲染》

    精读《React 八种条件渲染》

    作者: 分类:前端开发 点击: 512 次
    wordpress CMS主题:ssmay主题

      扫描下面的二维码,“关注”我的百家号。

      1 引言

      本期精读的文章是:8 React conditional rendering methods

      介绍了八种 React 条件渲染方式。

      模版条件渲染非常常见,遇到的时候往往会随机选择一种方式使用,那么怎么写会有较好的维护性呢?先一起了解下有哪八种条件渲染方式吧!

      2 概述

      IF/ELSE

      既然 JSX 支持 js 与 html 混写,那么交替使用就能解决条件渲染的问题:

      function render() {
        if (renderComponent1) {
          return <Component1 />;
        } else {
          return <div />;
        }
      }
      

      return null

      如果不想渲染空元素,最好使用 null 代替空的 div

      function render() {
        if (renderComponent1) {
          return <Component1 />;
        } else {
          return null;
        }
      }
      

      这样对 React 渲染效率有提升。

      组件变量

      将组件赋值到变量,就可以在 return 前任意修改它了。

      function render() {
        let component = null;
      
        if (renderComponent1) {
          component = <Component1 />;
        }
      
        return component;
      }
      

      三元运算符

      三元运算符的语法如下:

      condition ? expr_if_true : expr_if_false
      

      用在 JSX 上也很方便:

      function render() {
        return renderComponent1 ? <Component1 /> : null;
      }
      

      但三元运算符产生嵌套时,理解成本会变得很高。

      &&

      这个是最常用了,因为代码量最少。

      function render() {
        return renderComponent1 && <Component1 />;
      }
      

      IIFE

      IIFE 含义是立即执行函数,也就是如下代码:

      (function myFunction(/* arguments */) {
        // ...
      })(/* arguments */);
      

      当深陷 JSX 代码中,又想写一大块逻辑时,除了回到上方,还可以使用 IIFE:

      function render() {
        return (
          <div>
            {(() => {
              if (renderComponent1) {
                return <Component1 />;
              } else {
                return <div />;
              }
            })()}
          </div>
        );
      }
      

      子组件

      这是 IIFE 的变种,也就是把这段立即执行函数替换成一个普通函数:

      function render() {
        return (
          <div>
            <SubRender />
          </div>
        );
      }
      
      function SubRender() {
        if (renderComponent1) {
          return <Component1 />;
        } else {
          return <div />;
        }
      }
      

      IF 组件

      做一个条件渲染组件 IF 代替 js 函数的 if

      <If condition={true}>
        <span>Hi!</span>
      </If>
      

      这个组件实现也很简单

      const If = props => {
        const condition = props.condition || false;
        const positive = props.then || null;
        const negative = props.else || null;
      
        return condition ? positive : negative;
      };
      

      高阶组件

      高阶组件,就是返回一个新组件的函数,并且接收一个组件作为参数。

      那么我们就能在高阶组件里写条件语句,返回不同的组件即可:

      function higherOrderComponent(Component) {
        return function EnhancedComponent(props) {
          if (condition) {
            return <AnotherComponent {...props} />;
          }
      
          return <Component {...props} />;
        };
      }
      

      3 精读

      这么多方法都能实现条件渲染,那么重点在于可读性与可维护性。

      比如通过调用函数实现组件渲染:

      <div>{renderButton()}</div>
      

      看上去还是比较冗余,如果使用 renderButton getter 定义,我们就可以这么写它:

      <div>{button}</div>
      

      其实我们想要的就是 button,而不是 renderButton。那么还可以进一步,干脆封装成 JSX 组件:

      <div>
        <Button />
      </div>
      

      是否要付出这些努力,取决于应用的复杂度。如果应用复杂度非常高,那你应当尽量使用最后一种封装,让每个文件的逻辑尽量独立、简单。

      如果应用复杂度比较低,那么注意不要过度封装,以免把自己绕进去。

      所以看来这又是一个没有固定答案的问题,选择何种方式封装,取决于应用复杂度。

      应用复杂度

      对任何代码封装,都会增加这段 连接逻辑 的复杂度。

      假定无论如何代码的复杂度都是恒定不变的,下面这段代码,连接复杂度为 0,而对于 render 函数而言,逻辑复杂度是 100:

      function render() {
        if (renderComponent) {
          return isOk ? <Component1 /> : <Component2 />;
        } else {
          return <div />;
        }
      }
      

      下面这段代码拆成了两个函数,逻辑复杂度对 render SubComponent 来说都是 50,但连接复杂度是 50:

      function render() {
        if (renderComponent) {
          return <SubComponent>;
        } else {
          return <div />;
        }
      }
      
      function SubComponent() {
        return isOk ? <Component1 /> : <Component2 />
      }
      

      可以看到,我们通过函数拆分,降低了每个函数的逻辑复杂度,但却提高了连接复杂度。

      下面来做一个比较,我们假设一个正常的程序员,可以一次性轻松记忆 10 个函数。如果再多,函数之间的调用关系就会让人摸不着头脑。

      应用较小时

      在应用代码量比较小时,假设一共有 10 个函数,如果做了逻辑抽象,拆分出了 10 个子函数,那么总逻辑复杂度不变,函数变成了 20 个。

      此时小王要修改此项目,他需要找到关键代码的位置。

      如果没有做逻辑抽象,小王一下子就记住了 10 个函数,并且很快完成了需求。

      如果应用做了逻辑抽象,他需要理解的逻辑复杂度是不变的,但是要读的函数变成了 20 个。小王需要像侦探一样在线索中不断跳转,他还是只找了 10 个关键函数,但一共也就 20 个函数,逻辑并不复杂,这值得吗?

      小王心里可能会嘀咕:简单的逻辑瞎抽象,害我文件找了半天!

      应用较大时

      此时应用代码量比较大,假设一共有 500 个函数,我们不考虑抽象后带来的复用好处,假设都无法复用,那么做了逻辑抽象后,那么总逻辑复杂度不变,函数变成了 1000 个。

      此时小王接到了需求,终于维护了一个大项目。

      小王知道这个项目很复杂,从一开始就没觉得能理解项目全貌,所以把自己当作一名侦探,准备一步步探索。

      现在有两种选择,一种是在未做逻辑抽象时探索,一种是在做过逻辑抽象后探索。

      如果没做逻辑抽象,小王需要面对 500 个这种函数:

      function render() {
        if (renderComponent) {
          return isOk ? <Component1 /> : <Component2 />;
        } else {
          return isReady ? <Component3 /> : <Component4 />;
        }
      }
      

      如果做了逻辑抽象,小王需要面对 1000 个这种函数:

      function render() {
        if (renderComponent) {
          return <Component1And2 />;
        } else {
          return <Component3And4 />;
        }
      }
      

      在项目庞大后,总函数数量并不会影响对线索的查找,而总线索深度也几乎总是固定的,一般在 5 层左右。

      小王理解 5 个或 10 个函数成本都差不多,但没有做逻辑抽象时,这 5 个函数各自参杂了其他逻辑,反而影响对函数的理解。

      这时做逻辑抽象是合适的。

      4 总结

      所以总的来说,笔者更倾向使用子函数、子组件、IF 组件、高阶组件做条件渲染,因为这四种方式都能提高程序的抽象能力。

      往往抽象后的代码会更具有复用性,单个函数逻辑更清晰,在切面编程时更利于理解。

      当项目很简单时,整个项目的理解成本都很低,抽象带来的复杂度反而让项目变成了需要切面编程的时候,就得不偿失了。

      总结一下:

      • 当项目很简单,或者条件渲染的逻辑确认无法复用时,推荐在代码中用 && 或者三元运算符、IIFE 等直接实现条件渲染。
      • 当项目很复杂时,尽量都使用 子函数、子组件、IF 组件、高阶组件 等方式做更有抽象度的条件渲染。
      • 在做逻辑抽象时,考虑下项目的复杂度,避免因为抽象带来的成本增加,让本可以整体理解的项目变得支离破碎。

      5 更多讨论

      讨论地址是:精读《React 八种条件渲染》 · Issue #90 · dt-fe/weekly

      如果你想参与讨论,请点击这里,每周都有新的主题,周末或周一发布。

      本文转载于:猿2048https://www.mk2048.com/blog/blog_h10ckkikaa.html



      欢迎“关注”我的百家号。

      头条二维码
      加入我的QQ群
      头条二维码
      关注我的百家号

    文章作者:sunny
    本文地址:http://wanlimm.com/77202006218403.html
    版权所有 © 转载时必须以链接形式注明作者和原始出处!

    上一篇:
    下一篇:
    wordpress CMS主题:ssmay主题

    或许你会感兴趣的文章:

    发表评论

    电子邮件地址不会被公开。 必填项已用*标注

    This site uses Akismet to reduce spam. Learn how your comment data is processed.