Reactで要素の表示/非表示|toggleの実装方法

Reactで要素の表示/非表示を切り替える様々な方法を解説。useState、条件分岐、アニメーション付きtoggleの実装を初心者向けに説明します。

Learning Next 運営
26 分で読めます

Reactでボタンをクリックしたら何かが表示されるような機能を作りたいと思ったことはありませんか?

「詳細情報を表示・非表示したい」 「メニューの開閉を実装したい」 「FAQをクリックしたら回答を見せたい」

こんな表示切替機能は、Webアプリケーションでとてもよく使われる基本的な機能です。

この記事では、Reactでの要素表示/非表示の実装方法を、基本的なものから応用的なものまで詳しく解説します。 初心者の方でもすぐに実践できるよう、コード例をたくさん用意しました。

ぜひ最後まで読んで、使いやすいtoggle機能をマスターしてくださいね!

基本的なtoggle機能の作り方

まずは、一番シンプルな表示/非表示の切り替えから始めましょう。 これができれば、あらゆるtoggle機能の土台になりますよ。

useStateを使った基本形

import React, { useState } from 'react';

const BasicToggle = () => {
  const [isVisible, setIsVisible] = useState(false);
  
  const toggleVisibility = () => {
    setIsVisible(!isVisible);
  };
  
  return (
    <div>
      <button onClick={toggleVisibility}>
        {isVisible ? '非表示' : '表示'}
      </button>
      
      {isVisible && (
        <div>
          <p>こちらが表示/非表示される内容です。</p>
        </div>
      )}
    </div>
  );
};

これが最も基本的な実装方法です。

useStateでboolean値(true/false)を管理します。 isVisibletrueのときだけ要素が表示されるという仕組みです。

&&演算子を使った条件分岐で、表示・非表示を制御しています。 シンプルですが、とても実用的な方法ですね。

三項演算子を使った書き方

const ToggleWithTernary = () => {
  const [isOpen, setIsOpen] = useState(false);
  
  return (
    <div>
      <button onClick={() => setIsOpen(!isOpen)}>
        {isOpen ? '閉じる' : '開く'}
      </button>
      
      {isOpen ? (
        <div>
          <h3>詳細情報</h3>
          <p>こちらが詳細な情報です。</p>
        </div>
      ) : null}
    </div>
  );
};

三項演算子(? :)を使った書き方もできます。 表示する内容が複雑な場合は、この書き方の方が読みやすいかもしれません。

ボタンのクリック時に、直接setState関数を呼び出しています。 簡潔で分かりやすい書き方ですね。

様々な表示切替パターン

基本的な実装を覚えたら、もう少し複雑なパターンにも挑戦してみましょう。 実際のWebアプリケーションでよく使われる例をご紹介します。

パターン1: 複数のタブを切り替える

const MultiToggle = () => {
  const [activeTab, setActiveTab] = useState('tab1');
  
  const tabs = {
    tab1: '1つ目のタブ内容',
    tab2: '2つ目のタブ内容',
    tab3: '3つ目のタブ内容'
  };
  
  return (
    <div>
      <div className="tab-buttons">
        {Object.keys(tabs).map(tabKey => (
          <button
            key={tabKey}
            onClick={() => setActiveTab(tabKey)}
            className={activeTab === tabKey ? 'active' : ''}
          >
            {tabKey}
          </button>
        ))}
      </div>
      
      <div className="tab-content">
        {tabs[activeTab]}
      </div>
    </div>
  );
};

複数のタブを切り替える場合は、文字列で状態を管理します。 オブジェクト形式でタブの内容を定義すると、管理しやすくなります。

アクティブなタブにはactiveクラスを付けて、CSSでスタイリングできるようにしています。 実用的でよく使われるパターンですよ。

パターン2: アコーディオンメニュー

const AccordionMenu = () => {
  const [openItems, setOpenItems] = useState({});
  
  const toggleItem = (itemId) => {
    setOpenItems(prev => ({
      ...prev,
      [itemId]: !prev[itemId]
    }));
  };
  
  const menuItems = [
    { id: 'item1', title: '質問1', content: '回答1の内容です。' },
    { id: 'item2', title: '質問2', content: '回答2の内容です。' },
    { id: 'item3', title: '質問3', content: '回答3の内容です。' }
  ];
  
  return (
    <div>
      {menuItems.map(item => (
        <div key={item.id} className="accordion-item">
          <button
            onClick={() => toggleItem(item.id)}
            className="accordion-title"
          >
            {item.title}
            <span>{openItems[item.id] ? '▲' : '▼'}</span>
          </button>
          
          {openItems[item.id] && (
            <div className="accordion-content">
              {item.content}
            </div>
          )}
        </div>
      ))}
    </div>
  );
};

アコーディオンメニューは、複数の項目を独立して開閉できる仕組みです。

openItemsというオブジェクトで、各項目の開閉状態を管理します。 スプレッド演算子(...)を使って、既存の状態を保持しながら特定の項目だけを更新しています。

FAQページなどでよく見る形式ですね。

パターン3: サイドバーの開閉

const SidebarToggle = () => {
  const [isSidebarOpen, setIsSidebarOpen] = useState(false);
  
  const toggleSidebar = () => {
    setIsSidebarOpen(!isSidebarOpen);
  };
  
  return (
    <div className="app-container">
      <button onClick={toggleSidebar} className="menu-button">
        {isSidebarOpen ? '✕' : '☰'}
      </button>
      
      <div className={`sidebar ${isSidebarOpen ? 'open' : 'closed'}`}>
        <nav>
          <ul>
            <li><a href="#home">ホーム</a></li>
            <li><a href="#about">会社概要</a></li>
            <li><a href="#contact">お問い合わせ</a></li>
          </ul>
        </nav>
      </div>
      
      <main className={`main-content ${isSidebarOpen ? 'shifted' : ''}`}>
        <h1>メインコンテンツ</h1>
        <p>こちらがメインの内容です。</p>
      </main>
    </div>
  );
};

サイドバーの開閉は、CSSクラスを動的に切り替える方法で実装します。

isSidebarOpenの状態に応じて、openclosedクラスを付け替えます。 メインコンテンツもshiftedクラスで位置を調整できるようにしています。

スマートフォン向けサイトでよく使われる実装方法です。

CSSと組み合わせたアニメーション

ただの表示・非表示だけでなく、スムーズなアニメーションも付けてみましょう。 ユーザー体験が格段に向上しますよ。

基本的なフェードイン・フェードアウト

const AnimatedToggle = () => {
  const [isVisible, setIsVisible] = useState(false);
  
  return (
    <div>
      <button onClick={() => setIsVisible(!isVisible)}>
        {isVisible ? '非表示' : '表示'}
      </button>
      
      <div className={`content ${isVisible ? 'visible' : 'hidden'}`}>
        <p>アニメーション付きの内容です。</p>
      </div>
    </div>
  );
};

アニメーションを付けるときは、要素を完全に削除するのではなく、CSSクラスを切り替えます。

対応するCSSはこんな感じです。

.content {
  opacity: 0;
  max-height: 0;
  overflow: hidden;
  transition: opacity 0.3s ease, max-height 0.3s ease;
}

.content.visible {
  opacity: 1;
  max-height: 200px;
}

.content.hidden {
  opacity: 0;
  max-height: 0;
}

opacityで透明度を、max-heightで高さを制御しています。 transitionでアニメーションの時間と動きを指定します。

この方法だと、滑らかなフェードイン・フェードアウトが実現できます。

スライドアニメーション

const SlideToggle = () => {
  const [isOpen, setIsOpen] = useState(false);
  
  return (
    <div className="slide-container">
      <button onClick={() => setIsOpen(!isOpen)}>
        {isOpen ? '閉じる' : '開く'}
      </button>
      
      <div className={`slide-content ${isOpen ? 'open' : 'closed'}`}>
        <div className="slide-inner">
          <h3>スライドする内容</h3>
          <p>こちらがスライドで表示される内容です。</p>
        </div>
      </div>
    </div>
  );
};

スライドアニメーション用のCSSです。

.slide-content {
  overflow: hidden;
  transition: max-height 0.3s ease;
}

.slide-content.closed {
  max-height: 0;
}

.slide-content.open {
  max-height: 300px;
}

.slide-inner {
  padding: 20px;
}

overflow: hiddenで、はみ出した部分を隠します。 max-heightを0から300pxに変化させることで、上から下にスライドする効果を作っています。

パディングは内側の要素(.slide-inner)に設定するのがポイントです。

カスタムフックで再利用しやすく

同じような機能を何度も書くのは面倒ですよね。 カスタムフックを作って、再利用しやすくしてみましょう。

useToggleフック

import { useState } from 'react';

const useToggle = (initialState = false) => {
  const [state, setState] = useState(initialState);
  
  const toggle = () => setState(!state);
  const setTrue = () => setState(true);
  const setFalse = () => setState(false);
  
  return { state, toggle, setTrue, setFalse };
};

このカスタムフックを使うと、toggle機能がとても簡潔に書けます。

// 使用例
const ToggleWithCustomHook = () => {
  const { state: isVisible, toggle } = useToggle(false);
  
  return (
    <div>
      <button onClick={toggle}>
        {isVisible ? '非表示' : '表示'}
      </button>
      
      {isVisible && (
        <div>
          <p>カスタムフックを使った実装です。</p>
        </div>
      )}
    </div>
  );
};

togglesetTruesetFalseの3つの関数が用意されているので、用途に応じて使い分けられます。 とても便利で、プロジェクト全体で再利用できますね。

複数の状態管理フック

const useMultiToggle = (initialStates = {}) => {
  const [states, setStates] = useState(initialStates);
  
  const toggle = (key) => {
    setStates(prev => ({
      ...prev,
      [key]: !prev[key]
    }));
  };
  
  const setTrue = (key) => {
    setStates(prev => ({
      ...prev,
      [key]: true
    }));
  };
  
  const setFalse = (key) => {
    setStates(prev => ({
      ...prev,
      [key]: false
    }));
  };
  
  return { states, toggle, setTrue, setFalse };
};

複数の項目を同時に管理したい場合は、このカスタムフックが便利です。

// 使用例
const MultiToggleExample = () => {
  const { states, toggle } = useMultiToggle({
    section1: false,
    section2: false,
    section3: false
  });
  
  return (
    <div>
      {Object.keys(states).map(key => (
        <div key={key}>
          <button onClick={() => toggle(key)}>
            {key} を {states[key] ? '非表示' : '表示'}
          </button>
          
          {states[key] && (
            <div>
              <p>{key} の内容です。</p>
            </div>
          )}
        </div>
      ))}
    </div>
  );
};

アコーディオンメニューやタブ機能など、複数の状態を管理する場合に重宝します。

実践的な応用例

ここからは、実際のWebアプリケーションでよく使われる、より実践的な例をご紹介します。 これらの例を参考に、自分のプロジェクトに応用してみてください。

モーダルウィンドウ

const Modal = ({ isOpen, onClose, children }) => {
  if (!isOpen) return null;
  
  return (
    <div className="modal-overlay" onClick={onClose}>
      <div className="modal-content" onClick={(e) => e.stopPropagation()}>
        <button className="modal-close" onClick={onClose}>
          ✕
        </button>
        {children}
      </div>
    </div>
  );
};

const ModalExample = () => {
  const [isModalOpen, setIsModalOpen] = useState(false);
  
  return (
    <div>
      <button onClick={() => setIsModalOpen(true)}>
        モーダルを開く
      </button>
      
      <Modal
        isOpen={isModalOpen}
        onClose={() => setIsModalOpen(false)}
      >
        <h2>モーダルの内容</h2>
        <p>こちらがモーダルウィンドウの内容です。</p>
      </Modal>
    </div>
  );
};

モーダルウィンドウは、isOpenがfalseのときにnullを返して完全に非表示にします。

オーバーレイ(背景)をクリックしたときに閉じるようにしています。 モーダルの中身をクリックしたときは閉じないよう、stopPropagation()で処理を止めています。

よく使われる実装パターンなので、覚えておくと便利ですよ。

ドロップダウンメニュー

const DropdownMenu = () => {
  const [isOpen, setIsOpen] = useState(false);
  
  const menuItems = [
    { label: 'プロフィール', action: () => console.log('プロフィール') },
    { label: '設定', action: () => console.log('設定') },
    { label: 'ログアウト', action: () => console.log('ログアウト') }
  ];
  
  return (
    <div className="dropdown">
      <button
        onClick={() => setIsOpen(!isOpen)}
        className="dropdown-button"
      >
        メニュー ▼
      </button>
      
      {isOpen && (
        <div className="dropdown-menu">
          {menuItems.map((item, index) => (
            <button
              key={index}
              onClick={() => {
                item.action();
                setIsOpen(false);
              }}
              className="dropdown-item"
            >
              {item.label}
            </button>
          ))}
        </div>
      )}
    </div>
  );
};

ドロップダウンメニューでは、項目をクリックしたときに以下の処理を行います。

  1. その項目の機能を実行する(item.action()
  2. メニューを閉じる(setIsOpen(false)

この順番で処理することで、スムーズなユーザー体験を提供できます。

画像ギャラリー

const ImageGallery = () => {
  const [selectedImage, setSelectedImage] = useState(null);
  
  const images = [
    { id: 1, src: 'image1.jpg', alt: '画像1' },
    { id: 2, src: 'image2.jpg', alt: '画像2' },
    { id: 3, src: 'image3.jpg', alt: '画像3' }
  ];
  
  return (
    <div>
      <div className="thumbnail-grid">
        {images.map(image => (
          <img
            key={image.id}
            src={image.src}
            alt={image.alt}
            onClick={() => setSelectedImage(image)}
            className="thumbnail"
          />
        ))}
      </div>
      
      {selectedImage && (
        <div className="lightbox" onClick={() => setSelectedImage(null)}>
          <div className="lightbox-content">
            <img
              src={selectedImage.src}
              alt={selectedImage.alt}
              className="lightbox-image"
            />
            <button
              onClick={() => setSelectedImage(null)}
              className="lightbox-close"
            >
              ✕
            </button>
          </div>
        </div>
      )}
    </div>
  );
};

画像ギャラリーでは、選択された画像の情報を状態として保持します。 nullのときは何も表示せず、画像が選択されたときにライトボックス(拡大表示)を表示します。

サムネイル画像をクリックすると拡大表示され、背景や×ボタンをクリックすると閉じる仕組みです。

パフォーマンス最適化のコツ

アプリケーションが大きくなってくると、パフォーマンスも気になってきます。 toggle機能を最適化するためのテクニックをご紹介しましょう。

useCallbackを使った最適化

const OptimizedToggle = ({ children }) => {
  const [isVisible, setIsVisible] = useState(false);
  
  const toggleVisibility = useCallback(() => {
    setIsVisible(prev => !prev);
  }, []);
  
  return (
    <div>
      <button onClick={toggleVisibility}>
        {isVisible ? '非表示' : '表示'}
      </button>
      
      {isVisible && (
        <div>
          {children}
        </div>
      )}
    </div>
  );
};

useCallbackを使うことで、関数の再作成を防げます。 コンポーネントが再レンダリングされても、同じ関数オブジェクトを使い回せるので効率的です。

子コンポーネントがある場合は、特に効果的な最適化方法ですよ。

遅延読み込みとの組み合わせ

const LazyToggle = () => {
  const [isVisible, setIsVisible] = useState(false);
  const [content, setContent] = useState(null);
  
  const loadContent = useCallback(async () => {
    if (!content) {
      // 実際にはAPIから取得
      const data = await fetch('/api/content');
      setContent(await data.text());
    }
  }, [content]);
  
  const toggleVisibility = useCallback(() => {
    setIsVisible(prev => {
      if (!prev) {
        loadContent();
      }
      return !prev;
    });
  }, [loadContent]);
  
  return (
    <div>
      <button onClick={toggleVisibility}>
        {isVisible ? '非表示' : '表示'}
      </button>
      
      {isVisible && (
        <div>
          {content ? (
            <div dangerouslySetInnerHTML={{ __html: content }} />
          ) : (
            <p>読み込み中...</p>
          )}
        </div>
      )}
    </div>
  );
};

内容を表示するときに初めてデータを取得する方法です。 最初からすべてのデータを読み込まないので、初期表示が早くなります。

「読み込み中...」の表示を入れることで、ユーザーに状況を伝えられます。 大きなデータを扱うときに有効な手法ですね。

まとめ:toggle機能をマスターしよう

Reactでの要素表示/非表示の実装方法をたくさんご紹介しました。 これらのパターンを覚えておけば、ほとんどのtoggle機能を作れるようになりますよ。

基本パターン

  • useStateと条件分岐を使った基本実装
  • 三項演算子を使った書き方
  • CSSクラスの動的切り替え

応用パターン

  • 複数タブの切り替え
  • アコーディオンメニュー
  • サイドバーの開閉

アニメーション

  • フェードイン・フェードアウト
  • スライドアニメーション
  • CSSトランジションとの組み合わせ

再利用とパフォーマンス

  • カスタムフックの作成
  • useCallbackを使った最適化
  • 遅延読み込みとの組み合わせ

基本的な実装から始めて、徐々に複雑な機能を追加していくのがおすすめです。

最初はuseState&&演算子を使った簡単な表示・非表示から始めてみてください。 慣れてきたら、アニメーションやカスタムフックにも挑戦してみましょう。

ユーザビリティとパフォーマンスの両方を考慮した、使いやすいtoggle機能を作ってくださいね!

関連記事