React component is duplicated with p5.js canvas
Asked Answered
R

2

5

I'm trying to create a React app with a single p5.js sketch. However, the component containing the p5 sketch is being duplicated on my page. Not sure why it would be rendered this way.

Here you can see the code: https://stackblitz.com/edit/react-ts-kocwqw?file=App.tsx,Sketch.tsx,index.tsx

enter image description here

Here are the react components definition:

App.tsx

import React = require('react');
import Sketch from './Sketch';

function App() {
  return (
    <div className="App">
      <Sketch />
    </div>
  );
}

export default App;

Sketch.tsx

import React = require('react');
import { useEffect } from 'react';
import p5 from 'p5';

const Sketch = () => {
  const p = (p5: any) => {
    let radius: number;
    p5.setup = () => {
      p5.createCanvas(p5.windowWidth / 2, p5.windowHeight / 2);
      p5.background(0);
      radius = 0;
    };

    p5.draw = () => {
      p5.ellipse(p5.width / 2, p5.height / 2, radius, radius);
      if (radius < 70) radius++;
    };
  };

  useEffect(() => {
    new p5(p);
  }, []);

  return <></>;
};

export default Sketch;

index.tsx

import * as React from 'react';
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';

import App from './App';

const rootElement = document.getElementById('root');
const root = createRoot(rootElement);

root.render(
  <StrictMode>
    <App />
  </StrictMode>
);

What could I be missing here?

Ratty answered 23/5, 2022 at 13:27 Comment(1)
When the Sketch component is first mounted, it makes one canvas. It then gets unmounted and mounted again. That way, the p function is ran twice. You should return a cleanup function in your useEffect call to remove the created canvas somehow.Besot
R
6

Solution was to remove <StrictMode> from index.tsx.

From the docs,

Strict mode can’t automatically detect side effects for you, but it can help you spot them by making them a little more deterministic. This is done by intentionally double-invoking...

Looks like Strict Mode is a safeguard to detect side effects.

Ratty answered 23/5, 2022 at 16:5 Comment(1)
This didn't solve the issue for me, still looking for another way, any ideas?Donets
D
2

Another solution without removing the strict mode can be found here

https://www.lloydatkinson.net/posts/2022/how-to-prevent-a-duplicated-canvas-when-using-p5-and-react-strict-mode/

Basically using the useEffect cleanup method the current p5 instance will be deleted and a new one will be created on the next hot reload.

Here is a working example:

import { useEffect, useRef } from "react";
import p5 from "p5";

function App() {
  const p5Ref = useRef();

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

    p.draw = () => {
      p.background(200);
      p.rect(p.mouseX, p.mouseY, 100, 100);
    };
  };

  useEffect(() => {
    const mp5 = new p5(Sketch, p5Ref.current);
    return mp5.remove;
  }, []);

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

export default App;
Donets answered 12/8, 2023 at 13:38 Comment(1)
This is the actual fix. Not the accepted answer.Boggle

© 2022 - 2024 — McMap. All rights reserved.