[React Bootstrap] Overlays を表示してみる | 心を無にして始める React

今回は、Overlay コンポーネントを表示します。

準備

まだ components フォルダがなければ作ります。

src を右クリックして New Folder

components と入力してフォルダを作ります。

今回は、components フォルダに Overlay のコンポーネントを作って、表示してみます。

公式のドキュメントはここ。

https://react-bootstrap.github.io/components/overlays/#overlay

Overlay コンポーネントをつくる

component フォルダに Overlay.js を作ります。

Overlay.js を心を無にして編集してみます。

import React from 'react';
import { Overlay as BootstrapOverlay } from 'react-bootstrap';

const Overlay = React.forwardRef(({
  children,
  ...otherProps
}, ref) => {

  return (
    <BootstrapOverlay ref={ref} {...otherProps}>
      <div className="position-absolute">
        {children}
      </div>
    </BootstrapOverlay>
  )
})

export default Overlay;

プロジェクトでは、コンポーネントを統一したデザインで利用することが多いです。

そのため、(React Bootstrap のコンポーネントをその場その場でカスタマイズしながら使うよりも、)プロジェクトでコンポーネントにしたものを使うほうが、変更をお手軽に漏れなくできることが多いです。

さらに、Overlay コンポーネントでは ref を使うため、 Button.js が ref を受け取れるように直しておきます。
// React.forwordRef を使います。

import React, { useEffect, useState } from 'react';
import {
   Button as BootstrapButton,
   Spinner,
} from 'react-bootstrap';

const Button = React.forwardRef(({
  children,
  onClick,
  ...otherProps
}, ref) => {

  const [isLoading, setLoading] = useState(false);

  const handleClick = () => {
    if (onClick) {
      setLoading(true);
    }
  }

  const cb = () => {
    setLoading(false);
  }

  useEffect(() => {
    if (isLoading) {
      onClick(cb);
    }
  }, [isLoading, onClick]);

  return (
    <BootstrapButton
      ref={ref}
      onClick={handleClick}
      {...otherProps}
      disabled={isLoading}
    >
      {isLoading ? <Spinner animation="border" size="sm" /> : children}
    </BootstrapButton>
  )
});

export default Button;

Overlay コンポーネントを表示する

それでは、 App.js を編集して Overlay コンポーネントを表示します。

import React, { useRef, useState } from 'react';
import './App.css';

import Overlay from './components/Overlay';
import Button from './components/Button';

const PLACEMENTS = ['top-start', 'top', 'top-end', 'right-start', 'right', 'right-end', 'bottom-end', 'bottom', 'bottom-start', 'left-end', 'left', 'left-start'];

function App() {
  const [show, setShow] = useState(false);
  const target = useRef(null);

  return (
    <>
      <div className="p-5 bg-dark">
        <div className="p-5 d-flex flex-column justify-content-center align-items-center h-100">
          <Button
            ref={target}
            variant="light"
            style={{ width: 512, height: 384 }}
            onClick={cb => {
              setShow(!show);
              cb();
            }}
          >
            <h1>BASE</h1>
          </Button>
        </div>
      </div>
      {
        PLACEMENTS.map(placement => {
          return (
            <Overlay target={target.current} show={show} placement={placement}>
              <div className="p-3 bg-danger text-light text-center rounded" style={{ width: 128 }}>
                {placement}
              </div>
            </Overlay>
          );
        })
      }
    </>
  );
}

export default App;

画面を確認してみます。

はい、できました。