こんにちは。アイレット株式会社デザイン事業部の鶴若です。
社内でのReactでの開発が増加してきている為、インストールからの導入を復習を兼ねて簡単なバナー表示を作って振り返ってみたいと思います。

既存のサイトの一部に導入する

今回は、いくつかある導入方法の中で一番手軽なCDNからのscript読み込みからの既存サイトの一部に導入する方法をしてみようと思います。

scriptを読み込み

開発中は、development用ファイルで問題ないですが、本番公開時には圧縮済みのproduction用ファイルを利用するのが望ましいです。
envなどで環境変数によってビルド時に書き出し分ける仕組みを用意しておくと、修正ミスなどのリスクを減らす事が出来るので何かしらの対応をしておくと良いでしょう。

React本体の読み込み

開発用

<a href="https://unpkg.com/react@18/umd/react.development.js">https://unpkg.com/react@18/umd/react.development.js</a>
<a href="https://unpkg.com/react-dom@18/umd/react-dom.development.js">https://unpkg.com/react-dom@18/umd/react-dom.development.js</a>

本番用

<a href="https://unpkg.com/react@18/umd/react.production.min.js">https://unpkg.com/react@18/umd/react.production.min.js</a>
<a href="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js">https://unpkg.com/react-dom@18/umd/react-dom.production.min.js</a>

JSXを利用

JSXを利用することによってコード量を減らし、HTMLライクな構文のため、構造を理解しやすいと言うメリットがあります。
下記のscriptを読み込むか、JavaScriptのbabelを利用してビルドする仕組みを用意する必要です。

<a href="https://unpkg.com/babel-standalone@6/babel.min.js">https://unpkg.com/babel-standalone@6/babel.min.js</a>

HTMLの記述

reactを出力するコンテナ用のdivとscriptを合わせると下記のようになります。
(scriptはの前に入れましょう)

<body>
  <div id="banner"></div>
  <!-- 開発時はdevelopment.jsを使用 -->
  <a href="https://unpkg.com/react@18/umd/react.development.js">https://unpkg.com/react@18/umd/react.development.js</a>
  <a href="https://unpkg.com/react-dom@18/umd/react-dom.development.js">https://unpkg.com/react-dom@18/umd/react-dom.development.js</a>
  <!-- 公開時はproduction.jsを使用 -->
  <a href="https://unpkg.com/react@18/umd/react.production.min.js">https://unpkg.com/react@18/umd/react.production.min.js</a>
  <a href="https://unpkg.com/react@18/umd/react.production.min.js">https://unpkg.com/react@18/umd/react.production.min.js</a>
  <!-- banner.jsの書き出しにbabelを利用してない場合はbabel.min.jsが必要 -->
  <a href="https://unpkg.com/babel-standalone@6/babel.min.js">https://unpkg.com/babel-standalone@6/babel.min.js</a>
  <a href="/js/banner.js">/js/banner.js</a>
</body>

これで最低限のHTMLの記述は完了です。

JavaScriptの記述

HTML上に何かを出力する最低限の記述だと下記のようになります。

class Banner extends React.Component {
  render() {
    return (
      <div className="banner">
        バナー
      </div>
    );
  }
}
const root = ReactDOM.createRoot(document.querySelector('#banner'));
root.render(<Banner />);

今までの記述での問題なく出来ていれば、下記のようなテキストが表示されます。

もう少しバナーっぽい挙動にするためにclass Bannerを下記のように改修しましょう。
用意されているバナーの配列からコンテンツを生成して、ドットをクリック、または5秒おきにstateを更新して、バナーを切り替えるという良くある感じのバナーになります。

class Banner extends React.Component {
  constructor(props) {
    super(props);
    this.items = [
      {name: 'バナー 1', img: '/img/banner-1.jpg', href: '#'},
      {name: 'バナー 2', img: '/img/banner-2.jpg', href: '#'},
      {name: 'バナー 3', img: '/img/banner-3.jpg', href: '#'}
    ];
    this.state = {
      current: 0
    };
  }
  componentDidMount() {
    // 5秒で自動更新
    const items = this.items;
    this.timer = setInterval(() => {
      const current = this.state.current;
      this.setState({ current: current === items.length - 1 ? 0 : current + 1 });
    }, 5000);
  }
  componentWillUnmount() {
    clearInterval(this.timer);
  }
  render() {
    const items = this.items;
    const banners = [];
    const dots = [];
    const current = this.state.current;
    // バナーデータが格納されている配列からfor文を実行
    for (let i = 0; i < items.length; i++) {
      // バナーの中身を生成
      banners.push(<li>
        <a href="{items[i].href}">
          <img src="{items[i].img}" alt="{items[i].name}" />
        </a>
      </li>);
      // 切り替え用ドットを生成(クリックで更新)
      dots.push(<li> this.setState({ current: i })} key={`banner__dot-${i}`}>{i}</li>);
    }
    return (
      <div>
        <ul>
          // バナーの中身を挿入
          {banners}
        </ul>
        <ol>
          // 切り替え用ドットの中身を挿入
          {dots}
        </ol>
      </div>
    );
  }
}

CSS

cssで見た目も少し整えましょう。

.banner {
  width: 500px;
  margin: 0 auto;
}
.banner__list {
  padding: 0;
  margin: 0;
}
.banner__item {
  display: none;
  width: 100%;
  height: 100px;
  font-size: 20px;
  font-weight: bold;
}
.banner__item.is-current {
  display: block;
}
.banner__link {
  width: 100%;
  height: 100%;
  text-decoration: none;
  color: #000;
  display: flex;
  justify-content: center;
  align-items: center;
  transition: opacity 0.3s;
  cursor: pointer;
}
.banner__link:hover {
  opacity: 0.7;
}
.banner__dots {
  display: flex;
  justify-content: center;
  padding: 0;
  margin: 15px 0 0;
  list-style: none;
}
.banner__dot {
  margin: 0 8px;
  text-indent: -100%;
  overflow: hidden;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: #000;
  transition: opacity 0.3s;
  cursor: pointer;
  opacity: 0.5;
}
.banner__dot.is-current {
  opacity: 1;
}
.banner__dot:hover {
  opacity: 0.7;
}

完成品

上記の改修の結果テキストだけの表示から下記のようなバナーが表示されるようになりました。

今回は、ごく簡単なバナーの表示となりましたが、バナーの表示としては最低限の機能を持ったものになったかと思います。
(タッチでのアクションの追加や、JSON読み込みで表示内容の変更など、まだまだ改良の余地はありますが・・・)
次回は、もう1歩進んでSPAとルーティングについておさらいして行きたいと思います。