LoginSignup
0
0

複数の Root#render() 間で状態を共有する

Last updated at Posted at 2023-10-22

かなりのレアケースではありますが、例えば React を使った Web Components のカスタム要素を複数定義する場合などで、 Root#render() を複数回呼び出すことがあります。

src/index.tsx
import { createRoot } from "react-dom/client";
import { App } from "./App";
import { Button } from "./Button";

class MyButton extends HTMLElement {
  connectedCallback() {
    const shadow = this.attachShadow({ mode: "closed" });
    const root = createRoot(shadow);
    root.render(<Button />);
  }
}

class MyApp extends HTMLElement {
  connectedCallback() {
    const shadow = this.attachShadow({ mode: "closed" });
    const root = createRoot(shadow);
    root.render(<App />);
  }
}

customElements.define("my-button", MyButton);
customElements.define("my-app", MyApp);

また更にレアケースですが、このカスタム要素間で状態を共有したい場合があります。
当然ながら React 自体に Root#render() 間を跨いで状態を共有する方法はありませんが、React の外側で確保したメモリをポーリングするすることでそれっぽいことができます。

src/utils/str.ts
export let str = "aaa";

export function setStr(s: string) {
  str = s;
}
src/Button.tsx
import { setStr } from "./utils/str";

export function Button() {
  return (
    <button
      onClick={() => {
        setStr("bbb");
      }}
    >
      set "bbb"
    </button>
  );
}

src/App.tsx
import { useEffect, useState } from "react";
import { str } from "./utils/str";

export function App() {
  const [text, setText] = useState(str);

  useEffect(() => {
    const id = setInterval(() => {
      if (str !== text) {
        setText(str);
      }
    }, 1000);

    return () => {
      clearInterval(id);
    };
  }, []);

  return <span>テキスト: {text}</span>;
}

ボタンをクリックすると、少し遅れてテキストが bbb に変わります。

動作.gif

こんな怪しいアイデアが役に立って欲しくはありませんが、もし何かの刺激になれば幸いです。
今回紹介したコードはこちらのリポジトリにもプッシュしているので、興味のある方は覗いてみてください。
(もっと良いやり方をご存知の方がいれば、コメントにてお教えください。)

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0