阅读 281

React-条件渲染

前言

对于展示也没来说,通常有好几种展示状态(以列表请求为例): 当数据为空、接口报错、加载中、数据正常等状态,在渲染的时候需要正确判断并渲染对应的视图,也就是我们今天要讲的条件渲染。不同于 Vue 的 v-if 、 v-show 等框架提供的 api,React 的条件渲染都是 js 原生的方法再加上一点点的 hack。在 React 中有好几种方法可以实现条件表达式,并且不同的方法适用于不同的场景,取决于你需要处理什么样的问题。

概述

本文列举了 React 的几种条件渲染的实现方式:

  • If/Else
  • return null 阻止渲染
  • 变量
  • 三元运算符
  • 运算符(&&)
  • 自执行函数(IIFE)
  • 子组件

If/Else

创建两个组件, 一个是登录状态的提示组件,一个是未登录的提示组件

function UserGreeting(props) {
  return <h1>Welcome back!</h1>;
}

function GuestGreeting(props) {
  return <h1>Please sign up.</h1>;
}
复制代码

再创建一个 Greeting 组件,它会根据用户是否登录来决定显示上面的哪一个组件。

function Greeting(props) {
  const isLoggedIn = props.isLoggedIn;
  if (isLoggedIn) {
    return <UserGreeting />;
  }
  return <GuestGreeting />;
}
ReactDOM.render(
  // Try changing to isLoggedIn={true}:
  <Greeting isLoggedIn={false} />,
  document.getElementById('root')
);
复制代码

If/Else是最简便的实现条件渲染的方法,它的优势是,在简单场景下使用方便,并且每个程序员都理解这种使用方式。

return null 阻止渲染

如果想隐藏一个组件,你可以通过让该组件的 render 函数返回null,没必要使用一个空 div 或者其他什么元素去做占位符。

需要注意的是,即使返回了 null ,该组件“不可见”,但它的生命周期依然会运行。 举例如下,创建一个计时器组件

import { Component } from 'react'
class Number extends Component{
  constructor (props) {
    super(props)
  }
  componentDidUpdate() {
    console.log('componentDidUpdate');
  }
  render () {
      if (this.props.number % 2 !== 0) {
        return null
      } else {
        return (
          <div className="count_wrap">
            <h1>计时器:{this.props.number}</h1>
          </div>
        )
      }
    
  }
}
export default Number
复制代码

并在 App 组件中调用 Number组件

import { Component } from 'react'
import Number from './Number.js'

class App extends Component {
  constructor(props) {
    super(props)
    this.state = {
      count: 0
    }
    this.onClick = this.onClick.bind(this)
  }
  onClick () {
    this.setState(prevState => ({
      count: prevState.count + 1
    }));
  }
  render() {
    return (
      <div>
        <Number number={this.state.count} />
        <button onClick={this.onClick}>Count</button>
      </div>
    )
  }
}

ReactDOM.render( <App />, document.getElementById('root') );

复制代码

Number组件只有在偶数时才会展示。因为奇数时,render 函数返回了 null。但是,当你查看console 时会发现,componentDidUpdate函数每次都会执行,无论render函数返回什么。

image.png

返回 null 而不是空 div 的另一个好处是,这可以略微提升整个 React 应用的性能,因为 React 不需要在更新的时候 unmount 这个空 div。

变量

可以使用变量来储存 JSX 元素。 并且只有当条件为true的时候才去初始化,而其他的渲染部分并不会因此而改变。 举例如下,定义一个登录和注销的无状态组件:

function LoginButton(props) {
  return (
    <button onClick={props.onClick}>
      Login
    </button>
  );
}

function LogoutButton(props) {
  return (
    <button onClick={props.onClick}>
      Logout
    </button>
  );
}
复制代码

在下面的示例中,我们将创建一个名叫 LoginControl 的有状态的组件

它将根据当前的状态来决定我们定义的常量 button的值,并根据 button来渲染 <LoginButton /> 或者 <LogoutButton />

class LoginControl extends React.Component {
  constructor(props) {
    super(props);
    this.handleLoginClick = this.handleLoginClick.bind(this);
    this.handleLogoutClick = this.handleLogoutClick.bind(this);
    this.state = {isLoggedIn: false};
  }

  handleLoginClick() {
    this.setState({isLoggedIn: true});
  }

  handleLogoutClick() {
    this.setState({isLoggedIn: false});
  }

  render() {
    const isLoggedIn = this.state.isLoggedIn;
    let button = null;
    if (isLoggedIn) {
      button = <LogoutButton onClick={this.handleLogoutClick} />;
    } else {
      button = <LoginButton onClick={this.handleLoginClick} />;
    }
    return (
      <div>
        {button}
      </div>
    );
  }
}

ReactDOM.render(
  <LoginControl />,
  document.getElementById('root')
);
复制代码

三元运算符

我们可以使用三元运算符替代if/else代码块:

condition ? expr_if_true : expr_if_false
复制代码

举例如下,加入在刚才的例子中添加一段关于用户登录态的描述

render() {
  const isLoggedIn = this.state.isLoggedIn;
  return (
    <div>
      The user is <b>{isLoggedIn ? 'currently' : 'not'}</b> logged in.    </div>
  );
}
复制代码

整个运算符可以放在jsx的{}中,每一个表达式可以用()来包裹JSX来提升可读性。通过三元运算符,可以通过改变组件内的 isLoggedIn 状态来显示当前用户的登录态。

运算符(&&)

在 JavaScript 中,true && expression 总是会返回 expression, 而 false && expression 总是会返回 false

因此,如果条件是 true&& 右侧的元素就会被渲染,如果是 false,React 会忽略并跳过它。 例如,当你要么渲染一个组件,要么不做渲染,你可以使用&&运算符。举例如下我们在上面的计时器的例子可以修改如下:

import { Component } from 'react'
class Number extends Component{
  constructor (props) {
    super(props)
  }
  componentDidUpdate() {
    console.log('componentDidUpdate');
  }
  render () {
     const isEven = this.props.number % 2 === 0
     return isEven && (
        <div className="count_wrap">
          <h1>计时器:{this.props.number}</h1>
        </div>
      )    
  }
}
export default Number
复制代码

如果isEven结果为trueNumber组件才会展示;如果isEven结果为false,那么Number组件会被忽略。 最终渲染的结果与 return null 中的例子是一致的。

自执行函数(IIFE)

顾名思义,自执行函数就是在定义以后会被立刻执行,没有必要显式地调用他们。

也就是如下代码:

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

复制代码

在 React 中,你可以用一个大括号包裹一整个自执行函数,把所有逻辑都放在里面(if/else、switch、三元运算符等等),然后返回你需要渲染的东西。 举例如下:

render() {
  const isLoggedIn = this.state.isLoggedIn;
  return (
    <div>
        {
          (() => {
            let button = null;
            if (isLoggedIn) {
              button = <LogoutButton onClick={this.handleLogoutClick} />;
            } else {
              button = <LoginButton onClick={this.handleLoginClick} />;
            }
            return button
          })()
        }
    </div>
  );
}
复制代码

子组件

这是 IIFE 的变种,也就是把立即执行函数替换成一个普通函数,我们把上面 IIFE 的例子提取出 BtnControl子组件,通过 props 它接受足够的数据来供它展示。

import { Component } from 'react'
function LoginButton(props) {
  return (
    <button onClick={props.onClick}>
      Login
    </button>
  );
}

function LogoutButton(props) {
  return (
    <button onClick={props.onClick}>
      Logout
    </button>
  );
}

class BtnControl extends Component {
  render() {
    const { isLoggedIn, onLogin, onLogout } = this.props;
    const subLogoutClick = () => {
      onLogout()
    }
    const subLoginClick = () => {
      onLogin()
    }

    let button = null;
    if (isLoggedIn) {
      button = <LogoutButton onClick={subLogoutClick} />;
    } else {
      button = <LoginButton onClick={subLoginClick} />;
    }
    return (
      <div>
        {button}
      </div>
    );
  }
}
export default BtnControl

复制代码

然后 LoginControl 组件的 render 中引入 BtnControl组件

function render() {
  return (
    <div>
     <BtnControl isLoggedIn={isLoggedIn} onLogout={this.handleLogoutClick} onLogin={this.handleLoginClick}/>
    </div>
  );
}


复制代码

总结

在React中实现条件渲染有很多种实现方式,你可以自由选择任一方式,你可以基于这些理由来找到最适合当前场景的方案:

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