®️

Reflexにnpmのコンポーネントを入れたい!!

2024/05/15に公開

はじめに

reflexではnpmのコンポーネントを入れることができます。
https://reflex.dev/docs/wrapping-react/overview/#wrapping-react

バグなどでインポートできないケースがありますが、ほぼすべてのライブラリをラップすることが可能です。
ただ、何をどうすればよいか初見ではわかりにくいので簡単に説明します。

Reactを作るイメージ

Reactのソースコードを作るイメージで作成すると理解しやすいと思います。
まず、ライブラリのインストールを行い、次にファイルでライブラリをインポートし、さいごにタグを実装する流れと全く同じように、reflexでも実装できます。

参考にするコードはreflexのチュートリアルにあるSplineの以下の例を使用します。
https://github.com/splinetool/react-spline

index.js
import Spline from '@splinetool/react-spline';

export default function App() {
  return (
    <Spline
      scene="https://prod.spline.design/6Wq1Q7YGyM-iab9i/scene.splinecode"
    />
  );
}

ライブラリ情報はこちら
https://www.npmjs.com/package/@splinetool/react-spline

こちらはreflexのソースコードです。
使い方にフォーカスするため、継承するクラスの説明は割愛します。

index.py
import reflex as rx

class Spline(rx.Component):
    """Spline component."""

    library = "@splinetool/react-spline"
    lib_dependencies: list[str] = ["@splinetool/runtime"]

    tag = "Spline"
    is_default = True

    scene: rx.Var[str]

spline = Spline.create

def index():
    return spline(
        scene="https://prod.spline.design/joLpOOYbGL-10EJ4/scene.splinecode"
    )

インストールライブラリ

npmでインストールするライブラリを指定します。
Splineでは以下のようにnpmでインストールする記載があります。

npm install @splinetool/react-spline @splinetool/runtime

これは@splinetool/react-splineがまず必要で、依存関係として他のライブラリである@splinetool/runtimeを読み込んでいます。
そのままクラス変数に記載します。

index.py
    library = "@splinetool/react-spline"
    lib_dependencies: list[str] = ["@splinetool/runtime"]

これで、ライブラリのインストールが可能になります。

タグ情報

ライブラリインポート

次にSplineでは以下の様にタグをインポートします。

import Spline from '@splinetool/react-spline';

これを、メンバ変数のtagとis_defaultで指定します。
tagはそのままJSファイルのimport文に記載されます。
is_defaultはかぎかっこがあるか否かになります。
(npmに詳しくないため、このような記載になりました。)
is_defaultをTrueにすると

import {Spline} from '@splinetool/react-spline';

こうなり、インポートができなくなります。
ライブラリによって異なるので、それぞれ確認してください。

プロパティ

タグに入れるプロパティをメンバ変数に設定します。
Splineではsceneプロパティをタグに入れることで動作します。

<Spline
    scene="https://prod.spline.design/6Wq1Q7YGyM-iab9i/scene.splinecode"
/>

reflexではデフォルトのHTMLタグに使用しているプロパティ(srcやtargetなど)は実装していますが、個別のライブラリについては実装していません。
そこで、ライブラリ特有のプロパティをメンバ変数に指定してあげる必要があります。

index.py
    scene: rx.Var[str]

例ではsceneプロパティに文字列を代入するメンバ変数を設定しています。
こうすることでタグ生成の関数の引数にsceneを渡すことが可能になり、以下のようにタグに記載されます。

<タグ名
    メンバ変数名=メンバ変数の値
/>

タグ生成

最後にタグを生成しましょう。
コンポーネントインスタンスを生成し、UIに反映します。
例では変数に関数を代入していました。

index.py
spline = Spline.create

createはコンポーネントインスタンスを作成するファクトリ関数です。
タグの文字列生成や、stateの連携などを行います。

reflexでは引数の値に応じてインスタンス生成を分岐させているので、
_ _init __関数を使用する前に処理が必要です。
その処理をクラスメソッドのcreate関数で行っています。

変数に関数を代入しているのは見やすさと使いやすさのためです。
そのままSpline.create()を使用しても問題ありません。

reflexのデフォルトで使用している関数も同じように変数に格納しています。
例えば、以下の関数は

import reflex as rx
rx.box()

以下と同じ処理になっています。

from reflex.components.chakra.layout.box import Box
Box.create()

これだと繁雑なので、reflexのbox変数に上記関数を代入して使用しています。
変数に代入してもしなくても、動きとしては同じです。

本当に動いてるのか確認する最終手段

ソースコードを作成したあと、本当に動いているか画面でも確認しずらい時は、
生成したJSファイルを確認してみてください。
reflexプロジェクトに.webフォルダがあります。
その中に、reflexで生成したNext.js環境が格納されています。
pagesフォルダにindex.jsファイルがあるので、
そのファイルが参考にしているreactのコンポーネントのJSファイルと同じ実装になっているかを確認してみてください。
(※ページが異なる場合は、ファイル名も変わります。)

さいごに

reflexのReactライブラリのラップ方法を見ました。
Reactの実装と同じ流れでreflexでも実装ができるので、pythonのドキュメントが無くてもJSのソースコード例を参考に実装を進められることがわかりました。
reflexを使っていろいろなUIを作ってみたいですね。

ここまで見て下さりありがとうございました。

Discussion