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

使用高阶组件而屏蔽条件渲染 
组件能关注主要的逻辑目的

猜你喜欢

转载自blog.csdn.net/canduecho/article/details/79957955