序章

楽器のアコーディオンは圧縮および拡張します。 グラフィカル制御要素のアコーディオンも同様に折りたたみおよび拡張します。 これは、大きなコンテンツを分割し、ユーザーが自分に関連するセクションに集中できるようにするためのソリューションになります。

tabsコンポーネントと同様に、アコーディオンコンポーネントは、開閉を切り替えることができるさまざまなセクションで構成されています。 タブコンポーネントとは異なり、アコーディオンはコンテンツの複数のセクションを同時に表示することをサポートできます。

この記事では、開いたり閉じたりすることができるセクションを備えた、シンプルで再利用可能なアコーディオンコンポーネントを作成します。 次に、コンポーネントを変更して、一度に複数のセクションを開き、デフォルトで開くセクションを指定できるようにします。

前提条件

このチュートリアルを完了するには、次のものが必要です。

このチュートリアルは、ノードv16.6.2、npm v7.21.0、およびreactv17.0.2で検証されました。

ステップ1—プロジェクトの設定

このステップでは、 Create ReactAppを使用して新しいプロジェクトを作成します。 次に、プロジェクトのブートストラップ時にインストールされたサンプルプロジェクトと関連ファイルを削除します。

まず、新しいプロジェクトを作成します。 ターミナルで次のスクリプトを実行し、create-react-appを使用して新しいプロジェクトをインストールします。

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

プロジェクトが終了したら、次のディレクトリに移動します。

  1. cd react-accordion-component

新しいターミナルタブまたはウィンドウで、 CreateReactApp開始スクリプトを使用してプロジェクトを開始します。 ブラウザは変更時に自動更新されるため、作業中はこのスクリプトを実行したままにします。

  1. npm start

これにより、ローカルで実行されているサーバーが起動します。 プロジェクトがブラウザウィンドウで開かなかった場合は、http://localhost:3000/にアクセスしてプロジェクトを開くことができます。 これをリモートサーバーから実行している場合、アドレスはhttp://your_domain:3000になります。

ブラウザには、CreateReactAppの一部として含まれているテンプレートReactアプリケーションが読み込まれます。

React template project

完全に新しいカスタムコンポーネントのセットを構築するので、空のプロジェクトを作成できるように、ボイラープレートコードをクリアすることから始める必要があります。

まず、テキストエディタでsrc/App.jsを開きます。 これは、ページに挿入されるルートコンポーネントです。 すべてのコンポーネントはここから始まります。 App.jsの詳細については、 Create ReactAppを使用してReactプロジェクトをセットアップする方法を参照してください。

次のようなファイルが表示されます。

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を返す有効なページが表示されます。 最終的なコードは次のようになります。

src / App.js
import './App.css';

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

export default App;

テキストエディタを保存して終了します。

最後に、ロゴを削除します。 アプリケーションで使用することはありません。作業中に未使用のファイルを削除する必要があります。 長期的には混乱からあなたを救うでしょう。

ターミナルウィンドウで、次のコマンドを入力してロゴを削除します。

  1. rm src/logo.svg

プロジェクトが設定されたので、最初のコンポーネントを作成できます。

ステップ2—コンポーネントの構築

3つのコンポーネントを作成します。

  • Accordion:セクションコンポーネントを保持し、開いているセクションと閉じているセクションを管理します。
  • AccordionSection:クリック可能なセクションのタイトル、セクションが開いているときのセクションの内容を表示し、クリックイベントについてAccordionに報告します。
  • App:すべてを実際の例に結び付けるコンポーネント!

Accordionコンポーネント内のセクションは、AccordionSection内のクリック可能な領域に使用されるlabel属性を受け取る<div>タグになります。成分。

最も内側のコンポーネント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 / Accordion.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コンポーネントの作成に使用される子ノードを受け取ります。

このコンポーネントは、どのセクションが切り替えられて開かれたかを追跡しますが、一度に開いているセクションは1つだけ追跡します(現時点では)。

AccordionおよびAccordionSectionコンポーネントを作成したら、Appコンポーネントを作成して、動作を確認できます。

アコーディオンのデモでは、ワニに関するいくつかの事実を含む2つの子コンポーネントを含む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には2つのセクションがあり、クリックして開くと、さまざまな種類のワニに関するいくつかの事実が表示されます。

ステップ3—複数のオープンアコーディオンをサポートする

これまでに作成したものはすべて使用可能ですが、一度に開くことができるセクションは1つだけであり、デフォルトではすべてのセクションが折りたたまれているため、多少制限があります。

同時に開いている複数のセクションのサポートを追加するには、allowMultipleOpenという名前のプロパティをAccordionコンポーネントに追加します。このプロパティは、複数のセクションを許可するかどうかをアコーディオンに通知するために使用されます。オープンに。

有効にすると、状態は、操作されたセクションで状態を完全に上書きするのではなく、個々のキーのtrue/falseを切り替えます。

そこにいる間に、子ノードでisOpenプロパティをチェックするロジックを追加できます。 存在する場合、オープン状態は初期化され、セクションはすでにオープンとしてマークされています。

src / Accordion.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 で、この記事の実用的なデモとコードを見つけることができます。