0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

シンプルに「React + p5.js」を試す(Strict Mode で p5.js のキャンバスが 2つ生成されてしまう問題に対応)【p5.js:3】

Posted at

(この記事は p5.js の Advent Calendar 2025 の記事【3つ目】です)

はじめに

この記事の内容は、p5.js を「React」と組み合わせてみた話です。

いつもは、シンプルな HTML に p5.js のキャンバスを置く構成で試しているのですが、今回は React との組み合わせをシンプルな内容で試しました。それと、Strict Mode で p5.js のキャンバスが 2つ生成されてしまう問題への対応も行っています。

さっそく試す: React との組み合わせ

さっそく試していきます。

React のベースを準備する

自分がよく使うやり方だと npmコマンドを使ったものですが、今回は、前に導入したもののあまり使っていなかった「pnpm」を使うことにしました。

まずは、以下を進めます。

pnpm create vite my-app --template react

2025-12-10_21-36-04.jpg

上記の選択をして進めたところ、以下のようにローカルサーバーが立ち上がりました。

2025-12-10_21-38-07.jpg

そして http://localhost:5173/ にブラウザでアクセスすると、以下のページが表示されるのを確認できました。

2025-12-10_21-38-51.jpg

React を p5.js と組み合わせる

p5 の追加

ここから p5 - npm を追加したり、p5.js を扱うコードを準備します。

まずは p5 を追加するために、以下を実行します。

cd my-app
pnpm add p5

次にコードを準備して動作確認を行います。

お試し用のコードと補足

App.jsx の内容を、以下のように書きかえました。

App.jsx
import { useRef, useEffect } from "react";
import p5 from "p5";

function Sketch() {
  const containerRef = useRef(null);

  useEffect(() => {
    const container = containerRef.current;
    let p5Instance = null;

    // 即座に new p5() せず、ごく短い時間(ここでは 10ms)だけ
    // 待ってから作る。StrictModeの「即座にアンマウント」が
    // 起きた場合、このタイマーごとキャンセルされるため、
    // 1つ目のキャンバスは作成自体されなくなる。
    const timerId = setTimeout(() => {
      if (!container) return;

      const sketch = (p) => {
        p.setup = () => {
          p.createCanvas(400, 400);
        };

        p.draw = () => {
          p.background(220);
          p.ellipse(p.mouseX, p.mouseY, 50, 50);
        };
      };

      p5Instance = new p5(sketch, container);
    }, 10);

    return () => {
      // タイマー待ちの間にアンマウントされたら、
      // 作成そのものを中止
      clearTimeout(timerId);

      // 作成済みとなっていたなら削除
      if (p5Instance) {
        p5Instance.remove();
      }
    };
  }, []);

  return <div ref={containerRef} />;
}

export default Sketch;

p5.js を扱う部分ではインスタンスモードを使用し、p5.js のスケッチを特定の DOM要素(ここでは div)に紐付けられるようにしています。

また、上記のコードでは、以下により発生する「Strict Mode で p5.js のキャンバスが 2つ生成されてしまう問題」に対処しています。その内容の補足は、コード内にコメントで書いています。

●開発環境で 2 回発生するエフェクトへの正しい対応
 https://ja.react.dev/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development

2025-12-11_01-31-59.jpg

上記のページに「エフェクトを 1 回だけ実行する方法(例えばフラグを作って、そのフラグを使って処理を 1回だけに制限する等)」で対処するのではなく、「再マウントされても正しく動作するようエフェクトを修正する方法」で対処するべき、ということが書いてあったため、フラグを使わずにできる方法を実装してみました。
※ この対処で <StrictMode>。。。 </StrictMode> を削除する、というのも避けました

なお、タイマーの処理を入れているのは、「マウント → 即アンマウント → 再マウント」という処理が行われる中での p5.js の読み込み処理との兼ね合いで、クリーンアップが意図通りにはたらかなかったためです(※ 1回目のキャンバス作成処理が、クリーンアップ処理の後に完了したと思われる挙動で、クリーンアップが意図通りにはたらかない状況になっているようでした)。

開発環境での動作確認

あとは以下を実行して動作確認をするだけです。
※ 本番ビルドのコマンドではないので、Strict Mode の「マウント → 即アンマウント → 再マウント」が行われる処理になります

pnpm dev

ブラウザで http://localhost:5173/ にアクセスしたところ、以下のように p5.js のキャンバスが 1つだけ表示され、意図した表示になっていることを確認できました。

2025-12-11_01-37-56.jpg

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?