如何使用 React 构建手风琴组件

介绍

音乐乐器合并压缩和扩展. 图形控制元素合并类似地崩溃和扩展. 这可以是分解大内容的解决方案,并允许用户专注于与他们相关的部分。

类似于 tabs 组件,一个合并组件由不同的部分组成,可以转移到打开和关闭,与 Tabs 组件不同,合并可以支持同时显示多个内容部分。

在本文中,您将创建一个简单的可重复使用的符号组件,其中有可以转移开关的部分,然后我们将修改该组件以支持同时打开多个部分,并指定哪些部分应该默认打开。

前提条件

要完成本教程,您将需要:

本教程已通过 Node v16.6.2, npm v7.21.0 和 react v17.0.2 进行验证。

步骤1 - 设置项目

在此步骤中,您将使用 Create React App创建一个新项目,然后将删除在启动项目时安装的样本项目和相关文件。

要开始,创建一个新项目. 在终端中,运行以下脚本以使用create-react-app安装新项目:

1npx create-react-app react-accordion-component

项目完成后,更改到目录:

1cd react-accordion-component

在新的终端卡或窗口中,使用 Create React App start script启动项目。

1npm start

如果项目未在浏览器窗口中打开,您可以通过访问 http://localhost:3000/ 打开它. 如果您正在从远程服务器运行,则地址将是 http://your_domain:3000

您的浏览器将加载 React 模板应用程序,作为 Create React App 的一部分:

React template project

您将构建一组全新的自定义组件,因此您需要从清除一些锅炉板代码开始,以便您可以有一个空的项目。

要开始,请在文本编辑器中打开src/App.js。 这是注入到页面的根组件。 所有组件将从这里开始。 您可以找到有关App.js的更多信息,请参阅How To Set Up a React Project with Create React App(如何使用Create React App设置反应项目(LINK0))。

你會看到這樣的檔案:

 1[label src/App.js]
 2import logo from './logo.svg';
 3import './App.css';
 4
 5function App() {
 6  return (
 7    <div className="App">
 8      <header className="App-header">
 9        <img src={logo} className="App-logo" alt="logo" />
10        <p>
11          Edit <code>src/App.js</code> and save to reload.
12        </p>
13        <a
14          className="App-link"
15          href="https://reactjs.org"
16          target="_blank"
17          rel="noopener noreferrer"
18        >
19          Learn React
20        </a>
21      </header>
22    </div>
23  );
24}
25
26export default App;

删除./logo.svg导入标识行,然后更换返回声明中的所有内容,以返回一组div标签和一个h1

 1[label src/App.js]
 2import './App.css';
 3
 4function App() {
 5  return (
 6    <div>
 7      <h1>Accordion Demo</h1>
 8    </div>
 9  );
10}
11
12export default App;

保存和退出文本编辑器。

最后,删除标志. 你不会在你的应用程序中使用它,你应该在工作时删除未使用的文件。

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

1rm src/logo.svg

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

步骤2 - 构建组件

我们将创建三个组成部分:

  • Accordion:它将保留我们的部分组件,并管理哪些部分是开放的和关闭的.
  • AccordionSection:它将显示可点击的部分标题,部分内容,当该部分是开放的,并回报到 Accordion关于点击事件
  • App:组件将所有东西结合成一个工作示例!

Accordion组件内的部分将简单地成为<div>标签,它将收到一个标签属性,用于AccordionSection组件内的可点击区域。

让我们从创建我们最内在的组件开始,‘AccordionSection’:

 1[label src/AccordionSection.js]
 2import React from 'react';
 3import PropTypes from 'prop-types';
 4
 5class AccordionSection extends React.Component {
 6  static propTypes = {
 7    children: PropTypes.instanceOf(Object).isRequired,
 8    isOpen: PropTypes.bool.isRequired,
 9    label: PropTypes.string.isRequired,
10    onClick: PropTypes.func.isRequired,
11  };
12
13  onClick = () => {
14    this.props.onClick(this.props.label);
15  };
16
17  render() {
18    const {
19      onClick,
20      props: { isOpen, label },
21    } = this;
22
23    return (
24      <div
25        style={{
26          background: isOpen ? '#fae042' : '#6db65b',
27          border: '1px solid #008f68',
28          padding: '5px 10px',
29        }}
30      >
31        <div onClick={onClick} style={{ cursor: 'pointer' }}>
32          {label}
33          <div style={{ float: 'right' }}>
34            {!isOpen && <span>&#9650;</span>}
35            {isOpen && <span>&#9660;</span>}
36          </div>
37        </div>
38        {isOpen && (
39          <div
40            style={{
41              background: '#6db65b',
42              border: '2px solid #008f68',
43              marginTop: 10,
44              padding: '10px 20px',
45            }}
46          >
47            {this.props.children}
48          </div>
49        )}
50      </div>
51    );
52  }
53}
54
55export default AccordionSection;

此组件将收到一个标签属性,该属性将创建该部分的可点击标题。

主组件将处理哪些部分被打开或关闭的跟踪,并将状态作为布尔属性isOpen返回到AccordionSection

「AccordionSection」的任何小组件只会显示,如果该部分已转移到打开,并且属性「isOpen」是真实的。

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

 1[label src/Accordion.js]
 2import React from 'react';
 3import PropTypes from 'prop-types';
 4
 5import AccordionSection from './AccordionSection';
 6
 7class Accordion extends React.Component {
 8  static propTypes = {
 9    children: PropTypes.instanceOf(Object).isRequired,
10  };
11
12  constructor(props) {
13    super(props);
14
15    const openSections = {};
16
17    this.state = { openSections };
18  }
19
20  onClick = label => {
21    const {
22      state: { openSections },
23    } = this;
24
25    const isOpen = !!openSections[label];
26
27    this.setState({
28      openSections: {
29        [label]: !isOpen
30      }
31    });
32  };
33
34  render() {
35    const {
36      onClick,
37      props: { children },
38      state: { openSections },
39    } = this;
40
41    return (
42      <div style={{ border: '2px solid #008f68' }}>
43        {children.map(child => (
44          <AccordionSection
45            isOpen={!!openSections[child.props.label]}
46            label={child.props.label}
47            onClick={onClick}
48          >
49            {child.props.children}
50          </AccordionSection>
51        ))}
52      </div>
53    );
54  }
55}
56
57export default Accordion;

我们的Accordion组件将收到儿童节点,用于创建我们的互动AccordionSection组件。

此组件追踪哪些部分已被转移打开,但只跟踪一次(目前)打开的单个部分。

创建了我们的AccordionAccordionSection组件,我们现在可以创建我们的App组件,并看到事情在行动!

对于我们的合奏演示,我们将创建一个合奏,其中包括两个儿童组件,其中包含一些关于合奏者的事实:

 1[label src/App.js]
 2import Accordion from './Accordion';
 3
 4function App() {
 5  return (
 6    <div>
 7      <h1>Accordion Demo</h1>
 8      <Accordion>
 9        <div label='Alligator Mississippiensis'>
10          <p>
11            <strong>Common Name:</strong> American Alligator
12          </p>
13          <p>
14            <strong>Distribution:</strong> Texas to North Carolina, US
15          </p>
16          <p>
17            <strong>Endangered Status:</strong> Currently Not Endangered
18          </p>
19        </div>
20        <div label='Alligator Sinensis'>
21          <p>
22            <strong>Common Name:</strong> Chinese Alligator
23          </p>
24          <p>
25            <strong>Distribution:</strong> Eastern China
26          </p>
27          <p>
28            <strong>Endangered Status:</strong> Critically Endangered
29          </p>
30        </div>
31      </Accordion>
32    </div>
33  );
34}
35
36export default App;

合并包含两个部分,一旦点击打开,会显示一些关于不同种类的鱼的事实。

步骤 3 – 支持多个开放式协议

迄今为止我们创建的一切都是可用的,但有些有限,因为一次只能打开一个部分,并且每个部分默认情况下会崩溃。

为了增加对同时打开的多个部分的支持,我们将为Accordion组件添加一个名为allowMultipleOpen的属性,该组件将被用来告诉合并符是否应该允许多个部分打开。

当启用时,状态将切换个别键为 true/false,而不是完全重写状态与被交互的部分。

当我们在那里时,我们可以添加一些额外的逻辑来检查孩子节点的isOpen属性。

 1[label src/Accordion.js]
 2import React from 'react';
 3import PropTypes from 'prop-types';
 4
 5import AccordionSection from './AccordionSection';
 6
 7class Accordion extends React.Component {
 8  static propTypes = {
 9    allowMultipleOpen: PropTypes.bool,
10    children: PropTypes.instanceOf(Object).isRequired,
11  };
12
13  constructor(props) {
14    super(props);
15
16    const openSections = {};
17
18    this.props.children.forEach(child => {
19      if (child.props.isOpen) {
20        openSections[child.props.label] = true;
21      }
22    });
23
24    this.state = { openSections };
25  }
26
27  onClick = label => {
28    const {
29      props: { allowMultipleOpen },
30      state: { openSections },
31    } = this;
32
33    const isOpen = !!openSections[label];
34
35    if (allowMultipleOpen) {
36      this.setState({
37        openSections: {
38          ...openSections,
39          [label]: !isOpen
40        }
41      });
42    } else {
43      this.setState({
44        openSections: {
45          [label]: !isOpen
46        }
47      });
48    }
49  };
50
51  render() {
52    const {
53      onClick,
54      props: { children },
55      state: { openSections },
56    } = this;
57
58    return (
59      <div style={{ border: '2px solid #008f68' }}>
60        {children.map(child => (
61          <AccordionSection
62            isOpen={!!openSections[child.props.label]}
63            label={child.props.label}
64            onClick={onClick}
65          >
66            {child.props.children}
67          </AccordionSection>
68        ))}
69      </div>
70    );
71  }
72}
73
74export default Accordion;

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

 1[label src/App.js]
 2import Accordion from './Accordion';
 3
 4function App() {
 5  return (
 6    <div>
 7      <h1>Accordion Demo</h1>
 8      <Accordion allowMultipleOpen>
 9        <div label='Alligator Mississippiensis' isOpen>
10          <p>
11            <strong>Common Name:</strong> American Alligator
12          </p>
13          <p>
14            <strong>Distribution:</strong> Texas to North Carolina, US
15          </p>
16          <p>
17            <strong>Endangered Status:</strong> Currently Not Endangered
18          </p>
19        </div>
20        <div label='Alligator Sinensis'>
21          <p>
22            <strong>Common Name:</strong> Chinese Alligator
23          </p>
24          <p>
25            <strong>Distribution:</strong> Eastern China
26          </p>
27          <p>
28            <strong>Endangered Status:</strong> Critically Endangered
29          </p>
30        </div>
31      </Accordion>
32    </div>
33  );
34}
35
36export default App;

在那里,你有它,一个可重复使用的和谐组件,可以帮助你控制不合规的内容。

结论

在本文中,您创建了一个简单的可重复使用的合适件组件,其中有可以转移开启和关闭的部分。

继续学习,将这些和谐结合起来,以满足最复杂的场景。

您可以在 CodeSandbox上找到这篇文章中的工作演示文稿和代码。

Published At
Categories with 技术
Tagged with
comments powered by Disqus