#9 React中的条件渲染

React 中的条件渲染有以下几种方式:

  • if 语句
  • 三元操作符(ternary operator)
  • 逻辑 && 操作符
  • switch.. case.. 语句
  • 枚举(enums)
  • 多层条件渲染(multi-level conditional reandering)
  • 使用高阶组件

1.if 语句

在React中使用if语句条件渲染是最简单的。比如List组件如果没有任何items,可以提前return

function List({ list }) {
  if (!list) {
    return null;
  }
  return (
    <div>
      {list.map(item => <ListItem item={item} />)}
    </div>
  );
}

一个组件如果return null,将不会被渲染出来。如果你想给用户一些反馈,当list为空时,可以采用下面方式:

function List({ list }) {
  // list 为null的情况
  if (!list) {
    return null;
  }

  // list 为空的情况
  if (!list.length) {
    return <p>sorry, the list is empty</p>
  } else {
    return (
      <div>
        {list.map(item => <ListItem item={item} />)}
      </div>
    );
  }
}

if...else 是最基本的条件渲染方式。

2.三元操作符

可以使用三元运算符来代替上面的if...else... 条件渲染。

例如,你想从2种模式(edit, view)中切换,可以使用布尔值来决定那个组件被渲染:

function Item({ item, mode }) {
  const isEditMode = mode === 'EDIT';

  return (
    <div>
      { isEditMode
          ? <ItemEdit item={item} />
          : <ItemView item={item}>
      }
    </div>
  );
}

三元运算符使条件渲染更加的简洁,使得代码可以采用内联(inline)的方式表达出来

3.逻辑 '&&' 操作符

这个是当你想渲染一个组件或者什么也不渲染的情况

例如,有一个 LoadingIndicator 组件,返回加载文字或者什么也没有。当然可以使用if...else或者三元运算符,如下:

function LoadingIndicator({ isLoading }) {
  if (isLoading) {
    return (
      <div>
        <p>Loading...</p>
      </div>
    );
  } else {
    return null;
  }
}

function LoadingIndicator({ isLoading }) {
  return (
    <div>
      { isLoading
          ? <p>Loading...</p>
          : null
      }
    </div>
  );
}

使用 && 可以使返回 null 的情况的条件渲染更加的简洁

function LoadingIndicator({ isLoading }) {
  return (
    <div>
      { isLoading && <p>Loading...</p> }
    </div>
  );
}

4.switch...case语句

我们有可能遇到多种条件渲染的情况,例如依据不同的state渲染不同的Component.

例如,Notification 组件根据输入状态可能渲染Error,Warning,Info三种不同的组件。这个时候可以使用switch...case来进行多种条件渲染:

function Notification({ text, state }) {
  switch (state) {
    case 'info':
      return <Info text={text} />;
    case 'warning':
      return <Warning text={text} />;
    case 'info':
      return <Info text={text} />;
    default:
      return null;
  }
}

注意switch...case语句永远要加上default情况,因为React组件要么返回元素,要么返回null

另外值得注意的是,如果组件依据 state 属性 渲染时,最后添加上 React.PropTypes,即上面的组件后面添加:

Notification.propTypes = {
  text: React.PropTypes.string,
  state: React.PropTypes.oneOf(['info', 'warning', 'error'])
}

另一种将条件渲染结果内联在switch...case中的方法是,使用立即调用函数

function Notification({ text, state }) {
  return (
    <div>
      {(() => {
          switch (state) {
            case 'info':
              return <Info text={text} />;
            case 'warning':
              return <Warning text={text} />;
            case 'info':
              return <Info text={text} />;
            default:
              return null;
          }
      })()}
    </div>  
  );
}

switch...case帮助我们在多种条件中渲染中使用,但是多种条件渲染最好的方式是枚举

5.枚举(Enums)

在javascript中,对象的键值对可以用作枚举:

const ENUM = {
  a: '1',
  b: '2',
  c: '3'
};

枚举是多种条件渲染中很好的一种方式,上面的 Notification 组件可以写为:

function Notification({ text, state }) {
  return (
    <div>
      {{
        info: <Info text={text} />,
        warning: <Warning text={text} />,
        error: <Error text={text} />
      }[state]}
    </div>
  )
}

上面的 state 属性用于取回对象中的值,这比switch...case可读性更强。

因为对象的值依赖 text 属性,所以我们必须使用内联对象。如果我们不需要text属性,我们可以使用外部静态枚举:

const NOTIFICATION_STATES = {
  info: <Infor />,
  warning: <Warning />,
  error: <Error />,
};

function Notification({ state }) {
  return (
    <div>
      {NOTIFICATION_STATES[state]}
    </div>
  )
}

如果我们需要text属性,我们可以使用函数取回对象的值

const getSpecificNotification = (text) => ({
  info: <Info text={text} />,
  warning: <Warning text={text} />,
  error: <Error text={text} />,
});

function Notification({ state, text }) {
  return (
    <div>
      {getSpecificNotification(text)[state]}
    </div>  
  )
}

对象用作枚举,使用场景很灵活,下面例子:

function FooBarOrFooOrBar({ isFoo, isBar }) {
  const key = `${isFoo}-${isBar}`;
  return (
    <div>
      {{
        ['true-true']: <FooBar />,
        ['true-false']: <Foo />,
        ['false-true']: <Bar />,
        ['false-false']: null,
      }[key]}
    </div>
  );
}
FooBarOrFooOrBar.propTypes = {
  isFoo: React.PropTypes.boolean.isRequired,
  isBar: React.PropTypes.boolean.isRequired
}

6.多层条件渲染

我们有时候可能碰到嵌套条件选择渲染的情况。

例如,List组件可能显示一个list,或者一个empty text提示,或者什么多没有:

function List({ list }) {
  const isNull = !list;
  const isEmpty = !isNull && !list.length;

  return (
    <div>
      { isNull
          ? null
          : ( isEmpty
              ? <p>Sorry, the list is empty</p>
              : <div>{list.map(item => <ListItem item={item} />)}</div>
          )
      }
    </div>
  );
}

// 实例
<List list={null} /> // <div></div>
<List list={[]} /> // <div><p>Sorry, the list is empty</p></div>
<List list={['a', 'b', 'c']} /> // <div><div>a</div><div>b</div><div>c</div></div>

但是最好保持嵌套的层数最小化,这样代码可读性更强。可以将组件划分成更小的组件的方式

function List({ list }) {
  const isList = list && list.length;

  return (
    <div>
      { isList
          ? <div>{list.map(item => <ListItem item={item} />)}</div>
          : <NoList isNull={!list} isEmpty={list && !list.empty} />
      }
    </div>
  );
}

function NoList({ isNull, isEmpty }) {
  return (!isNull && isEmpty) && <p>Sorry, the list is empty</p>
}

7.高阶组件用作条件渲染

HOCs 在React中很适合条件渲染。HOCs的一种使用方式就是改变组件的外观。

例如使用高阶组件来展示一个加载显示器或者一个想要的组件:

function withLoadingIndicator(Component) {
  return function EnhancedComponent({ isLoading, ...props }) {
    if (!isLoading) {
      return <Component { ...props } />;
    }

    return <div><p>Loading...</p></div>;
  }
}

// 使用
const ListWithLoadingIndicator = withLoadingIndicator(List);

<ListWithLoadingIndicator
  isLoading={props.isLoading}
  list={props.list}
/>

这个示例中,List组件能关注渲染list上,而不必担心loading状态,另外,HOCs可以屏蔽list为null或empty的情况。

总结

使用上面的哪一种条件渲染可以根据下面的一些情况而定:

  • if-else

    • 最简单的条件渲染
    • 适用于新手
    • 使用if,从渲染方法中返回null提前退出函数
  • 三元操作符

    • 比if-else更好,优先使用
    • 比if-else更加简洁
  • 逻辑 '&&' 操作符

    • 不返回元素就返回null时使用
  • switch...case

    • 比较冗长
    • 可以通过立即调用函数内联使用
    • 使用枚举的方式代替这种方式更好
  • 枚举(enums)

    • 使用于不同的状态使用
    • 超过一种条件选择时使用
  • 多层次条件选择渲染

    • 避免使用这种方式对可读性的影响
    • 将组件划分为更小的组件,小组件自身带有条件选择
    • 偏向于使用高阶组件(HOCs)
  • HOCs

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

推荐阅读更多精彩内容