精读《React 八种条件渲染》

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

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,569评论 25 707
  • 越努力,越幸运 那天同学聚会,一个同学开着新买的马萨拉蒂闪亮登场,惹得一众朋友羡慕不已,都说这富二代的庐山真面目终...
    黛西_stacy阅读 202评论 0 0
  • 由青年微视出品的大型少儿古装益智剧——“少儿也三国”第二季携第一季狂潮火热回归,将于年后开播。第二季内容与第一季有...
    飞起来阅读 363评论 0 0