简体   繁体   中英

Why does changing state (useState) break my p5.js code? (React + p5.js application)

I created an Audio visualizer using p5.js and React and i need some help.

I am trying to change the state of my button input text from Play to Stop when the song starts playing & from Stop back to Play when the user decides to stop the song at that moment.

I am using react-p5 library and everything works great until i setState when a user clicks the play button. Since that causes a re render it breaks my canvas.

Is there a way to only re-render the button element? I am not too sure how i would go about changing the inner text of my button without a state change?

import React, { useState } from 'react';
import Sketch from 'react-p5';
import 'p5/lib/addons/p5.sound';
import Beat from '../../instrumental.mp3';

const App = () => {

  let width = 900;
  let height = 600;
  let song;
  let fft;

  const [flag, setFlag] = useState(true);

  const preload = (p) => {
    p.soundFormats('mp3');
    song = p.loadSound(Beat);
  }

  const setup = (p, canvasParentRef) => {
    // react-p5 conveniently initializes window.p5 thats why all the other p5's
    // have been changed to p in order to create an FFT object
    fft = new p5.FFT();
    // console.log(p.point)
    // use parent to render the canvas in this ref
        // without it p5 will render the canvas outside of this component
    const canvas = p.createCanvas(width, height).parent(canvasParentRef);
  }

  const playStop = () => {
    if (song.isPlaying()) {
      song.pause();
      setFlag(true);
      //p.noLoop();
    } else {
      song.play();
      setFlag(false);
      //p.loop();
    }
  }

  const draw = p => {
    p.background(0);
    // by default the stroke color is black
    // we need to change this in order to see the wave
    p.stroke(255, 204, 0);

    // no fill in between waves
    p.noFill();
    // returns an array with 1024 elements
    let wave = fft.waveform();

    p.beginShape();
    // By looping through the waveform data, we are able
    // to draw the waveform across the canvas
    for (let i = 0; i < width; i++) {
      // create an index that maps the for loop variable
      // to the index of the wave we want
      // value must be integer thats we we use floor
      let index = p.floor(p.map(i, 0, width, 0, wave.length));

      let x = i;
      let y = wave[index] * 100 + height / 2;
      p.vertex(x, y);
    }
    p.endShape();
  }

  return (
    <div className='outerbox'>
      <h1>Audio Visualizer</h1>
      <Sketch preload={preload} setup={setup} draw={draw}/>
      {flag ? <button onClick={playStop}>Play</button> : <button onClick={playStop}>Stop</button>}
    </div>
  );
}

export default App;

The sad thing is there aren't many resources available that includes react + p5.js

If anyone would like to take the time and clone this repository down in order to see what the problem might be, i would very much appreciate that.

Repo link: https://github.com/imperium11/audio-visualizer

  1. npm i
  2. npm run dev-build
  3. npm start

The issue here is that each time you update state in a functional component the function gets called again. As a result each time state changes you re-declare your preload<\/code> \/ setup<\/code> \/ draw<\/code> , because of the way react-p5<\/code> work, the running sketch will start using your updated draw<\/code> function. However, the updated draw function expects fft<\/code> to be defined, but the version of the fft<\/code> variable referenced by the new draw<\/code> function is undefined.

In this example I've packed all the locals into one object:

That way they would only be declared the first time your component function is called for each instance of it. This may be a bit of a hack, but it has the advantage of working for rapidly updating local variables (if you were changing local variables in your draw()<\/code> function, my understanding is that you would not want to make those into state variables, since high frequency state updates may impact performance.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM