Svelte でコンポーネントを作り React で糊付けする(ペライチ React で Snippet 編)

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187200/e6583627-82a1-4fc8-9e8b-519d9d62fc81.png" width="100%">

# はじめに

[前々回](https://qiita.com/uniho/items/bf00c2eb5b24e4212a2f)、[前回](https://qiita.com/uniho/items/c3dc6ace0990306a7afe) と、実用性とは程遠いコードを示して自己満足していましたが、今回も引き続き自己満足に浸りたいと思います（ついてきてくれる人だけついてきてくれていればそれで満足ですから!😭）。

Svelte には [Snippet](https://svelte.jp/docs/svelte/snippet) なるものがあるらしく、これを使えば React でいうところの Render prop 的なものが実現できます。

ペライチ React なサンプルコードを示し、前回と違う部分だけ解説します。

# サンプルコード

``` index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="initial-scale=1, width=device-width" />
  </head>
  <body>
    <div id="app"></div>

    <script type="importmap">
      {
        "imports": {
          "svelte/": "https://esm.sh/svelte@5/"
        }
      }
    </script>

    <script type="module">

import React from "https://esm.sh/react@19?dev";
import ReactDOM from "https://esm.sh/react-dom@19?dev";
import ReactDOMClient from "https://esm.sh/react-dom@19/client?dev";

import {VERSION, compile} from 'svelte/compiler';
import {mount, unmount, createRawSnippet} from 'svelte/';

//
const sv = compile(`
  <script>
    const {name = 'world', content} = $props();
  <\/script>   <!-- ペライチの場合、\ を付けないと script タグの閉じと誤認識されてしまう? -->

  <h1>Hello {name}!</h1>
  <div>
    {@render content?.()}
  </div>

  <style>
    h1 {
      color: red;
    }
  </style>
`, {/*css: 'injected'*/});

console.log(sv.js.code, sv.css.code);

//
const App = props => {

  const refDiv = React.useRef();

  React.useEffect(() => {
    const controller = new AbortController();

    (async() => {
      const SVC = await import(`data:text/javascript,${sv.js.code}`);
      const cssModule = await import(`data:text/css,${sv.css.code}`, {with: {type: 'css'}});
  
      document.adoptedStyleSheets = [...document.adoptedStyleSheets, cssModule.default];
  
      const content = createRawSnippet(() => ({
        render: () => `<p>Which do you like React or Svelte?</p>`,
      }));

      const svc = mount(SVC.default, {target: refDiv.current, props: {name: `Svelte ${VERSION}`, content}});

      controller.signal.addEventListener("abort", () => {
        document.adoptedStyleSheets = document.adoptedStyleSheets.filter(sheet => sheet !== cssModule.default);
        unmount(svc);
      });
    })();  

    return () => {
      controller.abort();
    };
  }, []);

  return React.createElement('div', {ref: refDiv});
}

const root = ReactDOMClient.createRoot(document.getElementById("app"))
root.render(React.createElement(App))

    </script>

  </body>
</html>
```

実行すると次のような感じの描画になるはずです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187200/4d614e4f-bb94-40fd-b544-395316d5654b.png)

# 解説

``` js
import {mount, unmount, createRawSnippet} from 'svelte/';
```
今回は `createRawSnippet` を使い Svelte の Snippet を生成します。

``` js
const sv = compile(`・・・
```
Svelte コンポーネントを JS にコンパイルする点は前回と同じですが、Svelte コンポーネントのソースが微妙に更新されていますので確認してください。

``` js
      const content = createRawSnippet(() => ({
        render: () => `<p>Which do you like React or Svelte?</p>`,
      }));

```
Svelte の Snippet を構築します。

``` js
      const svc = mount(SVC.default, {target: refDiv.current, props: {name: `Svelte ${VERSION}`, content}});
```
Svelte の Snippet を content prop として Svelte コンポーネントに渡します。

# 最後に
React & Svelte ハイブリッド、最強ですね。ではまた。
Let’s have fun with React and Svelte!