SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

再利用性とカプセル化のためのWeb Componentsを基礎から学ぶ

ReactからWeb Components製のコンポーネントを利用してみよう

再利用性とカプセル化のためのWeb Componentsを基礎から学ぶ 第7回

  • X ポスト
  • このエントリーをはてなブックマークに追加

ReactとWeb Componentsを組み合わせる

 これまでの連載で学んできた通り、Web Componentsはカスタム要素という形で、組み込みのHTML要素のように振る舞うことができます。Reactは、JSXというHTMLライクな記法の中に、HTML要素を書くことができます。それなら、リスト2のように、Web ComponentsをReactの中で使うこともできるのではないでしょうか。

[リスト2]最も素朴な利用方法
import React from 'react';
import './my-custom-element'; // Web Componentsを読み込む

export default function App() {
  return (
    <div>
      <my-custom-element></my-custom-element>
    </div>
  );
}

 リスト2のようなシンプルな例であれば、動作する可能性は高いです。しかし、Props(属性)を渡し始めたり、カスタム要素の中で発生したイベントのコールバックをReactで受け取ろうとすると、うまくいかなくなります。これは、React DOMで管理しているのはDOMツリーまでであり、Shadow DOMとの連携については、まだサポートできていないのが原因です。

 カスタム要素の属性をReact側の値と紐付ける方法については、React 19でのサポートを目指した取り組みがありますので、将来に期待しましょう。

@lit/react

 実は、第3回第4回で解説したLitというライブラリには、Reactとの連携をサポートするライブラリがあります。それが、@lit/reactです。このライブラリを使うと、ReactとWeb Componentsを比較的容易に組み合わせることができます。

 NPM管理下のプロジェクトであれば、リスト3のようにしてインストールできます。

[リスト3]@lit/reactのインストール
$ npm install lit @lit/react

 おそらく lit は無くても動きますが、今回のサンプルの中ではLitを利用してカスタム要素を作っているので、一緒にインストールしておきます。

 これで @lit/react を試す準備ができました。

Reactコンポーネントを作る

 さて、まずは属性を持たないプレーンなカスタム要素をReactコンポーネントとして使えるようにしてみましょう。リスト4のカスタム要素を対象にします。

[リスト4]src/simple-counter/my-counter-element.js
import {LitElement, html} from 'lit';

export class MyCounterElement extends LitElement {
  static properties = {
    count: { type: Number, reflect: true }
  }

  constructor() {
    super();
    this.count = 0;
  }

  render() {
    return html^^
      <p>カウント: ${this.count}</p>
      <button @click="${() => this.increment()}">+1</button>
    ^^;
  }

  increment() {
    this.count += 1;
  }
}

customElements.define('my-counter-element', MyCounterElement);

 ボタンを押すとカウントアップするだけの、シンプルなカスタム要素です。このカスタム要素をReactコンポーネントとして使えるようにするには、リスト5のようにします。

[リスト5]src/simple-counter/MyCounterElementComponent.jsx
import React from 'react';
import { createComponent } from '@lit/react';
import { MyCounterElement } from './my-counter-element';

export const MyCounterElementComponent = createComponent({ // (1)
  tagName: 'my-counter-element',
  elementClass: MyCounterElement,
  react: React,
})

 (1)の createComponent は、カスタム要素をReactコンポーネントとして使えるようにするための関数です。リスト5の例では、次の3つのパラメータを渡しています。

  • tagName:カスタム要素のタグ名
  • elementClass:カスタム要素のクラス
  • react:Reactライブラリ

 react が少しわかりづらいので補足しますと、Reactにはpreactのような互換ライブラリが存在しており、@lit/react は互換ライブラリ向けにコンポーネントを作成することもできるのです。今回はReactを使うので、React を渡しています。

 さて、リスト5で生み出したReactコンポーネントの MyCounterElementComponent を、実際に使ってみましょう。サンプルコードの src/App.jsx に配置してみました(リスト6)。

[リスト6]src/App.jsx
import { MyCounterElementComponent } from './simple-counter/MyCounterElementComponent';
import './App.css'

function App() {
  return (
    <>
      <h1>ReactからWeb Componentsを使う</h1>
      <h2>カスタム要素をコンポーネントとして配置する</h2>
      <MyCounterElementComponent />{/ (1) /}
    </>
  )
}

export default App

 (1)のように、Reactコンポーネントとして配置しました。ブラウザで http://localhost:5173/ を開くと、図3のように表示されます。

図3:カスタム要素をコンポーネントとして配置した例
図3:カスタム要素をコンポーネントとして配置した例

 まるで普通のReactコンポーネントのようですね。ただ、ここまでなら直接 <my-counter-element> を配置した場合にも、同じ挙動になりますので、まだ @lit/react の恩恵を受けているとは言い難いです。

Reactからプロパティを流し込む

 次に、Reactからpropsの形でプロパティを流し込んでみましょう。リスト7のように、名前を表示するだけの簡単なサンプルを用意しました。

[リスト7]src/properties/name-properties-element.js
import {LitElement, html} from 'lit';

export class MyNamePropertyElement extends LitElement {
  static properties = {
    person: {},
  }

  constructor() {
    super();
    this.person = { name: "名無し" };
  }

  render() {
    return html^^
      <p>こんにちは、${this.person.name}さん!</p>
    ^^;
  }
}

customElements.define('my-name-property-element', MyNamePropertyElement);

 ここで注目したいのが、プロパティとして定義した person に渡すのが、オブジェクトである、という点です。渡すのが文字列であれば、カスタム要素を直接書いても問題なく動くのですが、オブジェクト参照を渡すとなると、ReactとWeb Componentsの連携においては苦手な分野です。つまり、@lit/react がその苦手な分野をうまく吸収してくれるのか、という点が今回のポイントです。

 では、リスト8のように、@lit/react を使ってReactコンポーネントを作成して、動かしてみましょう。

[リスト8]src/properties/LitPropertiesAsReactProps.jsx
import React from 'react';
import { createComponent } from '@lit/react';
import { MyNamePropertyElement } from './name-properties-element';

const MyNamePropertyElementComponent = createComponent({ // (1)
  tagName: 'my-name-property-element',
  elementClass: MyNamePropertyElement,
  react: React,
})

export const LitPropertiesAsReactProps = () => {
  return (
    <MyNamePropertyElementComponent person={{ name: 'リアクト' }} />{/ (2) /}
  )
};

 (1)の createComponent の設定内容は、リスト5と同じです。(2)では、person というプロパティに、オブジェクトを渡しています。ブラウザで http://localhost:5173/ を開くと、図4のように表示されます。

図4:プロパティを渡した例
図4:プロパティを渡した例

 うまく表示されましたね。では、カスタム要素を直接書いた場合はどうなるのでしょうか。リスト9のように、returnの中を書き換えてみます。

[リスト9]カスタム要素にオブジェクトを渡す
  // (略)
  return (
    <my-name-property-element person={{ name: 'リアクト' }} />
  )
  // (略)

 ブラウザで http://localhost:5173/ を開くと、図5のように表示されます。

図5:カスタム要素にオブジェクトを渡した例
図5:カスタム要素にオブジェクトを渡した例

 名前が表示されませんでした。カスタム要素の内部でログを取ってみると、person には文字列の [object Object] が入っており、オブジェクトを渡すことには失敗しています。ここを上手くやってくれるのが、@lit/react です。

まとめ

 今回は、ReactとWeb Componentsの連携方法について、特にプロパティの話題を解説しました。次回は、コールバックイベントの取り扱いや、childrenとslotの関係について解説します。

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
再利用性とカプセル化のためのWeb Componentsを基礎から学ぶ連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト 中川幸哉(ナカガワユキヤ)

WINGSプロジェクトについて> 有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS Twitter: @yyamada(公式)、@yyamada/wings(メンバーリスト) Facebook

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/18797 2024/01/12 11:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング