MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

React组件的条件渲染

2022-06-144.7k 阅读

React 组件条件渲染的基础概念

在 React 应用开发中,条件渲染是一种非常重要的技术,它允许我们根据不同的条件来决定是否渲染某个组件,或者渲染不同的组件。这与我们在常规编程中使用条件语句(如 if - else)来控制代码执行流程类似,但在 React 中,它是应用于组件渲染层面的。

React 中的条件渲染遵循 JavaScript 的逻辑,我们可以使用 JavaScript 的各种条件判断语法,例如 if 语句、三元表达式 (condition? valueIfTrue : valueIfFalse) 以及 && 逻辑运算符等。这些方法在不同的场景下各有优势,下面我们来详细探讨。

使用 if 语句进行条件渲染

if 语句是最基础的条件判断方式,在 React 组件中,我们可以在 render 方法内使用 if 语句来决定是否渲染某个元素或组件。

import React, { Component } from'react';

class ConditionalRendering extends Component {
  constructor(props) {
    super(props);
    this.state = {
      isLoggedIn: false
    };
  }

  render() {
    if (this.state.isLoggedIn) {
      return <div>Welcome, user!</div>;
    }
    return <div>Please log in.</div>;
  }
}

export default ConditionalRendering;

在上述代码中,ConditionalRendering 组件的 state 中有一个 isLoggedIn 字段。根据 isLoggedIn 的值,render 方法会返回不同的 JSX 元素。如果 isLoggedIntrue,则返回 “Welcome, user!”;否则返回 “Please log in.”。

这种方式非常直观,适用于简单的条件判断,并且易于理解和维护。当条件判断逻辑较为复杂,例如需要进行多个条件的嵌套判断时,if 语句的优势就更加明显。

import React, { Component } from'react';

class ComplexConditionalRendering extends Component {
  constructor(props) {
    super(props);
    this.state = {
      userType: 'guest',
      isLoggedIn: false
    };
  }

  render() {
    if (this.state.isLoggedIn) {
      if (this.state.userType === 'admin') {
        return <div>Welcome, admin!</div>;
      } else if (this.state.userType === 'user') {
        return <div>Welcome, regular user!</div>;
      }
    }
    return <div>Please log in.</div>;
  }
}

export default ComplexConditionalRendering;

在这个例子中,我们有两层条件判断。首先判断用户是否登录,然后在已登录的情况下,根据用户类型(adminuser)返回不同的欢迎信息。如果用户未登录,则统一返回 “Please log in.”。

使用三元表达式进行条件渲染

三元表达式是一种简洁的条件判断方式,在 React 中也经常用于条件渲染。它的语法为 condition? valueIfTrue : valueIfFalse

import React, { Component } from'react';

class TernaryConditionalRendering extends Component {
  constructor(props) {
    super(props);
    this.state = {
      isLoggedIn: false
    };
  }

  render() {
    return (
      <div>
        {this.state.isLoggedIn? <div>Welcome, user!</div> : <div>Please log in.</div>}
      </div>
    );
  }
}

export default TernaryConditionalRendering;

这里通过三元表达式,根据 isLoggedIn 的值来决定渲染 “Welcome, user!” 还是 “Please log in.”。相比于 if 语句,三元表达式更加简洁,适合在简单条件判断且需要返回简单 JSX 元素的场景下使用。

然而,当条件判断逻辑变得复杂,或者需要返回的 JSX 结构较为复杂时,三元表达式的可读性会受到影响。例如:

import React, { Component } from'react';

class ComplexTernaryRendering extends Component {
  constructor(props) {
    super(props);
    this.state = {
      userType: 'guest',
      isLoggedIn: false
    };
  }

  render() {
    return (
      <div>
        {this.state.isLoggedIn? (
          this.state.userType === 'admin'? (
            <div>
              <h1>Welcome, admin!</h1>
              <p>You have full access.</p>
            </div>
          ) : (
            <div>
              <h1>Welcome, regular user!</h1>
              <p>You have limited access.</p>
            </div>
          )
        ) : (
          <div>
            <h1>Please log in.</h1>
            <p>Access is restricted.</p>
          </div>
        )}
      </div>
    );
  }
}

export default ComplexTernaryRendering;

在这个例子中,嵌套的三元表达式虽然实现了复杂的条件渲染逻辑,但代码的可读性明显下降。因此,在复杂场景下,需要谨慎使用三元表达式。

使用 && 逻辑运算符进行条件渲染

&& 逻辑运算符在 React 条件渲染中也有独特的应用。它的原理是:如果 && 左边的表达式为 true,则返回右边的表达式;如果左边为 false,则返回 false 本身,并且不会渲染右边的表达式。

import React, { Component } from'react';

class AndOperatorConditionalRendering extends Component {
  constructor(props) {
    super(props);
    this.state = {
      isLoggedIn: true
    };
  }

  render() {
    return (
      <div>
        {this.state.isLoggedIn && <div>Welcome, user!</div>}
      </div>
    );
  }
}

export default AndOperatorConditionalRendering;

在上述代码中,只有当 isLoggedIntrue 时,才会渲染 “Welcome, user!”。如果 isLoggedInfalse,则不会渲染任何内容。这种方式常用于在某个条件满足时渲染一个组件,而不满足时不需要渲染任何特定内容的场景。

&& 逻辑运算符还可以与其他条件判断方式结合使用,实现更复杂的逻辑。例如:

import React, { Component } from'react';

class ComplexAndOperatorRendering extends Component {
  constructor(props) {
    super(props);
    this.state = {
      userType: 'admin',
      isLoggedIn: true
    };
  }

  render() {
    return (
      <div>
        {this.state.isLoggedIn && (
          this.state.userType === 'admin'? (
            <div>
              <h1>Welcome, admin!</h1>
              <p>You can manage the system.</p>
            </div>
          ) : (
            <div>
              <h1>Welcome, user!</h1>
              <p>You can access normal features.</p>
            </div>
          )
        )}
      </div>
    );
  }
}

export default ComplexAndOperatorRendering;

这里首先通过 && 逻辑运算符判断用户是否登录,在已登录的情况下,再通过三元表达式根据用户类型渲染不同的内容。

条件渲染中的列表渲染

在 React 应用中,除了根据单个条件来渲染组件,我们还经常需要根据数据列表来进行条件渲染。例如,我们可能有一个用户列表,需要根据用户的状态(如激活或未激活)来决定是否在页面上显示该用户。

使用 map 方法结合条件渲染

map 方法是 JavaScript 数组的一个常用方法,它可以对数组中的每个元素执行一个回调函数,并返回一个新的数组。在 React 中,我们可以结合 map 方法和条件渲染来处理列表数据。

import React, { Component } from'react';

class UserList extends Component {
  constructor(props) {
    super(props);
    this.state = {
      users: [
        { id: 1, name: 'Alice', isActive: true },
        { id: 2, name: 'Bob', isActive: false },
        { id: 3, name: 'Charlie', isActive: true }
      ]
    };
  }

  render() {
    const activeUsers = this.state.users.map(user =>
      user.isActive && (
        <div key={user.id}>
          <p>{user.name} is active.</p>
        </div>
      )
    );
    return <div>{activeUsers}</div>;
  }
}

export default UserList;

在上述代码中,UserList 组件的 state 中有一个 users 数组,每个用户对象包含 idnameisActive 字段。通过 map 方法遍历 users 数组,对于每个用户,使用 && 逻辑运算符判断其 isActive 字段。如果用户是激活状态,则渲染包含用户名的 <p> 标签。注意,在 React 中渲染列表时,每个列表项都需要有一个唯一的 key 属性,这里我们使用用户的 id 作为 key

过滤列表后进行渲染

有时候,我们可能需要先对列表进行过滤,然后再进行渲染。例如,我们只希望显示年龄大于某个值的用户。

import React, { Component } from'react';

class FilteredUserList extends Component {
  constructor(props) {
    super(props);
    this.state = {
      users: [
        { id: 1, name: 'Alice', age: 25 },
        { id: 2, name: 'Bob', age: 18 },
        { id: 3, name: 'Charlie', age: 30 }
      ]
    };
  }

  render() {
    const filteredUsers = this.state.users.filter(user => user.age > 20).map(user => (
      <div key={user.id}>
        <p>{user.name} is {user.age} years old.</p>
      </div>
    ));
    return <div>{filteredUsers}</div>;
  }
}

export default FilteredUserList;

在这个例子中,我们首先使用 filter 方法过滤出年龄大于 20 的用户,然后对过滤后的用户列表使用 map 方法进行渲染。这种方式在处理复杂的列表数据和条件渲染时非常有用,可以确保只渲染符合特定条件的列表项。

条件渲染与组件状态和属性

基于组件状态的条件渲染

在 React 中,组件的状态(state)是驱动组件行为和渲染的重要因素。我们常常根据组件的状态来进行条件渲染。

import React, { Component } from'react';

class ToggleComponent extends Component {
  constructor(props) {
    super(props);
    this.state = {
      isToggled: false
    };
    this.toggle = this.toggle.bind(this);
  }

  toggle() {
    this.setState(prevState => ({
      isToggled:!prevState.isToggled
    }));
  }

  render() {
    return (
      <div>
        <button onClick={this.toggle}>
          {this.state.isToggled? 'Hide' : 'Show'}
        </button>
        {this.state.isToggled && <p>This is a toggled content.</p>}
      </div>
    );
  }
}

export default ToggleComponent;

ToggleComponent 中,isToggled 是组件的状态。当点击按钮时,toggle 方法会更新 isToggled 的状态。根据 isToggled 的值,按钮的文本会显示 “Hide” 或 “Show”,同时 <p> 标签内的内容也会根据状态进行条件渲染。

基于组件属性的条件渲染

组件的属性(props)同样可以用于条件渲染。父组件可以通过传递不同的属性值来控制子组件的渲染逻辑。

import React, { Component } from'react';

class MessageComponent extends Component {
  render() {
    return (
      <div>
        {this.props.isError? (
          <p style={{ color:'red' }}>{this.props.message}</p>
        ) : (
          <p style={{ color: 'green' }}>{this.props.message}</p>
        )}
      </div>
    );
  }
}

class ParentComponent extends Component {
  constructor(props) {
    super(props);
    this.state = {
      hasError: true
    };
  }

  render() {
    return (
      <div>
        <MessageComponent
          isError={this.state.hasError}
          message="This is a sample message."
        />
      </div>
    );
  }
}

export default ParentComponent;

在这个例子中,ParentComponentMessageComponent 传递了 isErrormessage 两个属性。MessageComponent 根据 isError 属性的值来决定以红色(错误信息)还是绿色(正常信息)显示 message。通过这种方式,我们可以灵活地在不同组件间传递条件,实现复杂的条件渲染逻辑。

条件渲染中的高阶组件与条件包裹

高阶组件用于条件渲染

高阶组件(HOC)是 React 中一种强大的模式,它是一个函数,接受一个组件作为参数并返回一个新的组件。我们可以利用高阶组件来实现条件渲染逻辑。

import React, { Component } from'react';

const withAuth = (WrappedComponent, isAuthenticated) => {
  return class extends Component {
    render() {
      if (isAuthenticated) {
        return <WrappedComponent {...this.props} />;
      }
      return <div>Access denied. Please log in.</div>;
    }
  };
};

class SecretPage extends Component {
  render() {
    return <div>This is a secret page.</div>;
  }
}

const AuthenticatedSecretPage = withAuth(SecretPage, true);

class App extends Component {
  render() {
    return (
      <div>
        <AuthenticatedSecretPage />
      </div>
    );
  }
}

export default App;

在上述代码中,withAuth 是一个高阶组件,它接受一个组件 WrappedComponent 和一个布尔值 isAuthenticated。如果 isAuthenticatedtrue,则渲染 WrappedComponent 并传递所有属性;否则,返回 “Access denied. Please log in.”。通过这种方式,我们可以在不修改 SecretPage 组件内部代码的情况下,实现基于认证状态的条件渲染。

条件包裹组件

有时候,我们可能需要根据条件包裹一个组件。例如,我们可能希望在用户登录时,将某个组件包裹在一个特定的布局组件内。

import React, { Component } from'react';

const LayoutComponent = ({ children }) => (
  <div style={{ border: '1px solid black', padding: '10px' }}>{children}</div>
);

class ContentComponent extends Component {
  render() {
    return <p>This is the content.</p>;
  }
}

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      isLoggedIn: true
    };
  }

  render() {
    const content = <ContentComponent />;
    return this.state.isLoggedIn? (
      <LayoutComponent>{content}</LayoutComponent>
    ) : (
      content
    );
  }
}

export default App;

在这个例子中,App 组件根据 isLoggedIn 的状态来决定是否将 ContentComponent 包裹在 LayoutComponent 内。如果用户已登录,则 ContentComponent 会被包裹在具有边框和内边距的 LayoutComponent 中显示;否则,直接显示 ContentComponent。这种条件包裹组件的方式在实现页面布局和权限控制等方面非常有用。

条件渲染的性能优化

避免不必要的渲染

在 React 中,不必要的渲染会影响应用的性能。当组件的 stateprops 发生变化时,组件会重新渲染。为了避免条件渲染导致的不必要渲染,我们可以使用 shouldComponentUpdate 方法(对于类组件)或 React.memo(对于函数组件)。

对于类组件:

import React, { Component } from'react';

class MyComponent extends Component {
  shouldComponentUpdate(nextProps, nextState) {
    // 只有当 props 中的某个特定字段变化时才重新渲染
    return this.props.someValue!== nextProps.someValue;
  }

  render() {
    return (
      <div>
        {this.props.someCondition && <p>Rendered based on condition.</p>}
      </div>
    );
  }
}

export default MyComponent;

MyComponent 中,shouldComponentUpdate 方法会在组件接收到新的 propsstate 时被调用。通过在这个方法中进行条件判断,我们可以决定是否真正需要重新渲染组件。在上述例子中,只有当 props 中的 someValue 发生变化时,组件才会重新渲染,从而避免了因其他无关 propsstate 变化导致的不必要渲染。

对于函数组件,我们可以使用 React.memo

import React from'react';

const MyFunctionalComponent = React.memo((props) => {
  return (
    <div>
      {props.someCondition && <p>Rendered based on condition.</p>}
    </div>
  );
});

export default MyFunctionalComponent;

React.memo 是一个高阶组件,它会对函数组件进行浅比较 props。如果 props 没有变化,组件将不会重新渲染。这在条件渲染场景中,可以有效地减少不必要的渲染,提高性能。

使用懒加载进行条件渲染

在应用中,有些组件可能比较大,加载时间较长。对于这类组件,我们可以使用懒加载结合条件渲染,只有在真正需要时才加载组件。

import React, { lazy, Suspense } from'react';

const BigComponent = lazy(() => import('./BigComponent'));

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      shouldLoad: false
    };
  }

  render() {
    return (
      <div>
        <button onClick={() => this.setState({ shouldLoad:!this.state.shouldLoad })}>
          {this.state.shouldLoad? 'Unload' : 'Load'} Big Component
        </button>
        {this.state.shouldLoad && (
          <Suspense fallback={<div>Loading...</div>}>
            <BigComponent />
          </Suspense>
        )}
      </div>
    );
  }
}

export default App;

在上述代码中,BigComponent 是一个通过 lazy 函数进行懒加载的组件。Suspense 组件用于在组件加载时显示一个加载提示(这里是 “Loading...”)。只有当 shouldLoadtrue 时,才会加载并渲染 BigComponent。这种方式可以有效地提高应用的初始加载性能,特别是在应用包含多个大型组件的情况下。

条件渲染在 React Router 中的应用

基于路由的条件渲染

React Router 是 React 应用中常用的路由管理库。在使用 React Router 时,我们经常需要根据当前路由来进行条件渲染。

import React from'react';
import { BrowserRouter as Router, Routes, Route } from'react-router-dom';

const Home = () => <div>This is the home page.</div>;
const About = () => <div>This is the about page.</div>;
const Admin = () => <div>This is the admin page. Access restricted.</div>;

const App = () => {
  return (
    <Router>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/admin" element={<Admin />} />
      </Routes>
    </Router>
  );
};

export default App;

在这个简单的 React Router 应用中,根据当前的 URL 路径,不同的组件会被渲染。例如,当 URL 为 “/” 时,Home 组件被渲染;当 URL 为 “/about” 时,About 组件被渲染;当 URL 为 “/admin” 时,Admin 组件被渲染。

路由保护与条件渲染

有时候,我们希望对某些路由进行保护,只有在用户满足特定条件(如已登录)时才能访问。这可以通过条件渲染结合 React Router 来实现。

import React, { useState } from'react';
import { BrowserRouter as Router, Routes, Route, Navigate } from'react-router-dom';

const Home = () => <div>This is the home page.</div>;
const About = () => <div>This is the about page.</div>;
const Admin = () => <div>This is the admin page. Access restricted.</div>;

const App = () => {
  const [isLoggedIn, setIsLoggedIn] = useState(false);

  return (
    <Router>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route
          path="/admin"
          element={isLoggedIn? <Admin /> : <Navigate to="/" replace />}
        />
      </Routes>
    </Router>
  );
};

export default App;

在上述代码中,/admin 路由受到保护。只有当 isLoggedIntrue 时,用户才能访问 Admin 组件;否则,会通过 Navigate 组件将用户重定向到首页(/)。这种方式在实现应用的权限控制和路由保护方面非常有效。

总结

React 组件的条件渲染是 React 应用开发中的核心技术之一,它允许我们根据不同的条件灵活地渲染组件。通过 if 语句、三元表达式、&& 逻辑运算符等多种方式,我们可以实现从简单到复杂的各种条件渲染逻辑。同时,结合列表渲染、组件状态和属性、高阶组件、性能优化以及 React Router 等知识,我们能够构建出功能强大、性能高效且用户体验良好的 React 应用。在实际开发中,需要根据具体的业务需求和场景,选择最合适的条件渲染方式,以达到最佳的开发效果。