如何使用React构建Accordion组件

来自菜鸟教程
跳转至:导航、​搜索

介绍

乐器手风琴压缩和扩展。 图形控制元素折叠式类似地折叠和展开。 这可以成为分解大型内容并允许用户专注于与他们相关的部分的解决方案。

类似于 tabs 组件,手风琴组件由不同的部分组成,可以切换为打开和关闭。 与选项卡组件不同,手风琴可以支持同时显示多个内容部分。

在本文中,您将创建一个简单的可重用手风琴组件,其中包含可以打开和关闭的部分。 然后我们将修改组件以支持一次打开多个部分并指定默认情况下应打开哪些部分。

先决条件

要完成本教程,您需要:

  • Node.js 安装在本地,您可以按照【X57X】如何安装Node.js 并创建本地开发环境【X126X】进行。
  • 对 React 有一定的了解。 你可以看看我们的 How To Code in React.js 系列。

本教程已使用 Node v16.6.2、npm v7.21.0 和 react v17.0.2 进行了验证。

第 1 步 — 设置项目

在这一步中,您将使用 Create React App 创建一个新项目。 然后,您将删除引导项目时安装的示例项目和相关文件。

首先,创建一个新项目。 在您的终端中,运行以下脚本以使用 create-react-app 安装新项目:

npx create-react-app react-accordion-component

项目完成后,进入目录:

cd react-accordion-component

在新的终端选项卡或窗口中,使用 Create React App 启动脚本 启动项目。 浏览器将自动刷新更改,因此请在您工作时保持此脚本运行:

npm start

这将启动一个本地运行的服务器。 如果项目没有在浏览器窗口中打开,您可以通过访问 http://localhost:3000/ 打开它。 如果您从远程服务器运行它,地址将是 http://your_domain:3000

您的浏览器将加载包含在 Create React App 中的模板 React 应用程序:

您将构建一组全新的自定义组件,因此您需要首先清除一些样板代码,以便您可以拥有一个空项目。

首先,在文本编辑器中打开 src/App.js。 这是注入页面的根组件。 所有组件都将从这里开始。 你可以在 How To Set Up a React Project with Create React App 中找到有关 App.js 的更多信息。

你会看到一个像这样的文件:

src/App.js

import logo from './logo.svg';
import './App.css';

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}

export default App;

删除行 import logo from './logo.svg';。 然后替换 return 语句中的所有内容以返回一组 div 标记和一个 h1。 这将为您提供一个返回 h1 的有效页面,该页面显示 Accordion Demo。 最终代码将如下所示:

src/App.js

import './App.css';

function App() {
  return (
    <div>
      <h1>Accordion Demo</h1>
    </div>
  );
}

export default App;

保存并退出文本编辑器。

最后,删除标志。 您不会在您的应用程序中使用它,并且您应该在工作时删除未使用的文件。 从长远来看,它将使您免于困惑。

在终端窗口中键入以下命令以删除徽标:

rm src/logo.svg

现在项目已经建立,您可以创建您的第一个组件。

第 2 步 — 构建组件

我们将创建三个组件:

  • Accordion:它将保存我们的部分组件并管理哪些部分是打开和关闭的。
  • AccordionSection:显示可点击的版块标题、版块打开时的版块内容,并向Accordion反馈点击事件。
  • App:将所有内容联系在一起成为一个工作示例的组件!

Accordion 组件内部的部分将只是 <div> 标记,它们将接收 label 属性,该属性将用于 AccordionSection 内部的可点击区域零件。

让我们从创建最里面的组件开始,AccordionSection

src/AccordionSection.js

import React from 'react';
import PropTypes from 'prop-types';

class AccordionSection extends React.Component {
  static propTypes = {
    children: PropTypes.instanceOf(Object).isRequired,
    isOpen: PropTypes.bool.isRequired,
    label: PropTypes.string.isRequired,
    onClick: PropTypes.func.isRequired,
  };

  onClick = () => {
    this.props.onClick(this.props.label);
  };

  render() {
    const {
      onClick,
      props: { isOpen, label },
    } = this;

    return (
      <div
        style={{
          background: isOpen ? '#fae042' : '#6db65b',
          border: '1px solid #008f68',
          padding: '5px 10px',
        }}
      >
        <div onClick={onClick} style={{ cursor: 'pointer' }}>
          {label}
          <div style={{ float: 'right' }}>
            {!isOpen && <span>&#9650;</span>}
            {isOpen && <span>&#9660;</span>}
          </div>
        </div>
        {isOpen && (
          <div
            style={{
              background: '#6db65b',
              border: '2px solid #008f68',
              marginTop: 10,
              padding: '10px 20px',
            }}
          >
            {this.props.children}
          </div>
        )}
      </div>
    );
  }
}

export default AccordionSection;

该组件将接收一个 label 属性,该属性将创建该部分的可点击标题。 onClick 事件处理程序属性用于向父组件报告是否已单击标签。

父组件将处理跟踪打开或关闭的部分,并将状态作为布尔属性 isOpen 反馈给 AccordionSection

AccordionSection 的任何子组件仅在该部分已切换为打开且属性 isOpen 为 true 时才会显示。

现在我们有一个用于我们的部分的组件,我们需要一个组件来容纳这些部分并管理哪些部分已打开或关闭的状态:

src/手风琴.js

import React from 'react';
import PropTypes from 'prop-types';

import AccordionSection from './AccordionSection';

class Accordion extends React.Component {
  static propTypes = {
    children: PropTypes.instanceOf(Object).isRequired,
  };

  constructor(props) {
    super(props);

    const openSections = {};

    this.state = { openSections };
  }

  onClick = label => {
    const {
      state: { openSections },
    } = this;

    const isOpen = !!openSections[label];

    this.setState({
      openSections: {
        [label]: !isOpen
      }
    });
  };

  render() {
    const {
      onClick,
      props: { children },
      state: { openSections },
    } = this;

    return (
      <div style={{ border: '2px solid #008f68' }}>
        {children.map(child => (
          <AccordionSection
            isOpen={!!openSections[child.props.label]}
            label={child.props.label}
            onClick={onClick}
          >
            {child.props.children}
          </AccordionSection>
        ))}
      </div>
    );
  }
}

export default Accordion;

伟大的! 我们的 Accordion 组件将接收子节点,这些子节点将用于创建我们的交互式 AccordionSection 组件。

该组件跟踪已切换打开的部分,但一次仅跟踪打开的单个部分(目前)。

创建了我们的 AccordionAccordionSection 组件后,我们现在可以创建我们的 App 组件并查看实际情况!

对于我们的手风琴演示,我们将创建一个带有两个子组件的 Accordion,其中包含一些关于鳄鱼的事实:

src/App.js

import Accordion from './Accordion';

function App() {
  return (
    <div>
      <h1>Accordion Demo</h1>
      <Accordion>
        <div label='Alligator Mississippiensis'>
          <p>
            <strong>Common Name:</strong> American Alligator
          </p>
          <p>
            <strong>Distribution:</strong> Texas to North Carolina, US
          </p>
          <p>
            <strong>Endangered Status:</strong> Currently Not Endangered
          </p>
        </div>
        <div label='Alligator Sinensis'>
          <p>
            <strong>Common Name:</strong> Chinese Alligator
          </p>
          <p>
            <strong>Distribution:</strong> Eastern China
          </p>
          <p>
            <strong>Endangered Status:</strong> Critically Endangered
          </p>
        </div>
      </Accordion>
    </div>
  );
}

export default App;

Accordion 包含两个部分,单击打开后,将显示有关不同种类短吻鳄的一些事实。

第 3 步 — 支持多个开放式手风琴

到目前为止,我们创建的所有内容都是可用的,但有些限制,因为一次只能打开一个部分,并且默认情况下每个部分都是折叠的。

为了增加对同时打开的多个部分的支持,我们将向 Accordion 组件添加一个名为 allowMultipleOpen 的属性,该属性将用于告诉手风琴它是否应该允许多个部分开放。

启用后,状态将切换各个键的真/假,而不是用与之交互的部分完全覆盖状态。

当我们在那里时,我们可以添加一些额外的逻辑来检查子节点的 isOpen 属性。 当存在时,打开状态将被初始化为已标记为打开的部分:

src/手风琴.js

import React from 'react';
import PropTypes from 'prop-types';

import AccordionSection from './AccordionSection';

class Accordion extends React.Component {
  static propTypes = {
    allowMultipleOpen: PropTypes.bool,
    children: PropTypes.instanceOf(Object).isRequired,
  };

  constructor(props) {
    super(props);

    const openSections = {};

    this.props.children.forEach(child => {
      if (child.props.isOpen) {
        openSections[child.props.label] = true;
      }
    });

    this.state = { openSections };
  }

  onClick = label => {
    const {
      props: { allowMultipleOpen },
      state: { openSections },
    } = this;

    const isOpen = !!openSections[label];

    if (allowMultipleOpen) {
      this.setState({
        openSections: {
          ...openSections,
          [label]: !isOpen
        }
      });
    } else {
      this.setState({
        openSections: {
          [label]: !isOpen
        }
      });
    }
  };

  render() {
    const {
      onClick,
      props: { children },
      state: { openSections },
    } = this;

    return (
      <div style={{ border: '2px solid #008f68' }}>
        {children.map(child => (
          <AccordionSection
            isOpen={!!openSections[child.props.label]}
            label={child.props.label}
            onClick={onClick}
          >
            {child.props.children}
          </AccordionSection>
        ))}
      </div>
    );
  }
}

export default Accordion;

随着我们的 Accordion 准备好接受新参数,我们可以更新我们的 App 组件以传递属性以允许打开多个部分以及标记第一个部分默认打开:

src/App.js

import Accordion from './Accordion';

function App() {
  return (
    <div>
      <h1>Accordion Demo</h1>
      <Accordion allowMultipleOpen>
        <div label='Alligator Mississippiensis' isOpen>
          <p>
            <strong>Common Name:</strong> American Alligator
          </p>
          <p>
            <strong>Distribution:</strong> Texas to North Carolina, US
          </p>
          <p>
            <strong>Endangered Status:</strong> Currently Not Endangered
          </p>
        </div>
        <div label='Alligator Sinensis'>
          <p>
            <strong>Common Name:</strong> Chinese Alligator
          </p>
          <p>
            <strong>Distribution:</strong> Eastern China
          </p>
          <p>
            <strong>Endangered Status:</strong> Critically Endangered
          </p>
        </div>
      </Accordion>
    </div>
  );
}

export default App;

你有它,一个可重复使用的手风琴组件,可以帮助你驯服不守规矩的内容。

结论

在本文中,您创建了一个简单的可重用手风琴组件,其中的部分可以打开和关闭。

通过嵌套这些手风琴来继续您的学习,以满足最复杂的场景。

您可以在 CodeSandbox 上找到本文中的工作演示和代码。