更新状态而不渲染整个 React 组件(useState)

时间:2021-05-05 13:40:45

标签: reactjs tone.js

我有一个组件,它实例化 Tone.js 库中的一些类(例如音频播放器和过滤器),并定义了一些作用于这些对象的函数,这些函数用作一组 UI 渲染按钮中的回调(见下方相关代码)。

其中两个按钮应该使用 useState hook(在 is3D 函数中)切换布尔状态 updateSpatial 以启用/禁用这些按钮之一。但是,此更新显然会导致组件完全重新渲染,从而重新实例化我的类,这会阻止已定义的函数在之后工作。

相比之下,我还尝试了 useRef hook,它允许在不重新渲染的情况下更新 is3D,但按钮的禁用状态不会更新,因为组件不会重新渲染。

有没有适合这种情况的模式?我的下一次尝试包括使用 HOC、Context 或 Redux,但我不确定这是最直接的。谢谢!

import React, {useState, useEffect, Fragment} from 'react'
import { Player, Destination} from "tone";

const Eagles = () => {

  const [is3D, setIs3D] = useState(false);

  useEffect(() => {
    // connect players to destination, ready to play
    connectBase();
  });
  
  // Instantiating players
  const player1= new Player({
    "url": "https://myapp.s3.eu-west-3.amazonaws.com/Aguabeber.m4a",
    "autostart": false,
    "onload": console.log("player 1 ready")    
  });
  const player2 = new Player({
    "url": "https://myapp.s3.eu-west-3.amazonaws.com/Aguas.m4a",
    "autostart": false,
    "onload": console.log("player 2 ready")
  });             
  // Functions
  function connectBase() {
    player1.disconnect();
    player1.connect(Destination);
    player2.disconnect();    
    player2.chain(Destination);
  }
  function playStudio() {
    player1.volume.value = 0;
    player2.volume.value = -60;    
  }
  function playCinema() {
    player1.volume.value = -60;
    player2.volume.value = 0;  
  }
  function  playstereo() {
    player1.volume.value = 0;
    player2.volume.value = -60;
    updateSpatial();
  }  
  function  playmyhifi() {
    player1.volume.value = -60;
    player2.volume.value = 0;
    updateSpatial();
  }  
  function start() {
      player1.start();
      player2.start();      
      playstereo();
    }  
  function stop() {
    player1.stop();
    player2.stop();      
    console.log("stop pressed")
  }
  // Update state to toggle button enabled`  
  function updateSpatial() {
    setIs3D((is3D) => !is3D);
  }

  return (
    <Fragment>       
          <ButtonTL onClick={start}>Play</ButtonTL>
          <ButtonTR onClick={stop}>Stop</ButtonTR>     
          <ButtonTL2 onClick={playstereo}>Stereo</ButtonTL2>
          <ButtonTR2 onClick={playmyhifi}>3D</ButtonTR2>             
          <ButtonLL disabled={is3D} onClick={playStudio}>Studio</ButtonLL>
          <ButtonLR onClick={playCinema}>Cinema</ButtonLR>                                                          
     </Fragment>
  )
}

1 个答案:

答案 0 :(得分:2)

我发现您的代码有两处错误。 首先,函数“正常”主体中的所有内容都将在每次渲染时执行。因此,需要状态和钩子。 状态允许您在渲染之间保留数据,而钩子允许您在状态更改时执行特定操作。

其次,useEffect(()=>console.log(hi)) 没有任何依赖关系,因此它会在每次渲染时运行。 useEffect(()=>console.log(hi),[]) 将仅在第一次渲染时执行。 useEffect(()=>console.log(hi),[player1]) 将在 player1 更改时执行。 useEffect(()=>console.log(hi),[player1, player2]) 将在 player1 或 player2 更改时执行。

小心带有依赖项的钩子。如果在钩子本身内设置依赖项之一的状态,它将创建一个无限循环

这里有一些更接近你想要的东西:

import React, {useState, useEffect, Fragment} from 'react'
import { Player, Destination} from "tone";

const Eagles = () => {

  const [is3D, setIs3D] = useState(false);
  const [player1]= useState(new Player({
    "url": "https://myapp.s3.eu-west-3.amazonaws.com/Aguabeber.m4a",
    "autostart": false,
    "onload": console.log("player 1 ready")    
  }));
  const [player2] = useState(new Player({
    "url": "https://myapp.s3.eu-west-3.amazonaws.com/Aguas.m4a",
    "autostart": false,
    "onload": console.log("player 2 ready")
  }));   

  useEffect(() => {
    // connect players to destination, ready to play
    connectBase();
  },[player1, player2]);
  
  // Instantiating players
            
  // Functions
  function connectBase() {
    player1.disconnect();
    player1.connect(Destination);
    player2.disconnect();    
    player2.chain(Destination);
  }
  function playStudio() {
    player1.volume.value = 0;
    player2.volume.value = -60;    
  }
  function playCinema() {
    player1.volume.value = -60;
    player2.volume.value = 0;  
  }
  function  playstereo() {
    player1.volume.value = 0;
    player2.volume.value = -60;
    updateSpatial();
  }  
  function  playmyhifi() {
    player1.volume.value = -60;
    player2.volume.value = 0;
    updateSpatial();
  }  
  function start() {
      player1.start();
      player2.start();      
      playstereo();
    }  
  function stop() {
    player1.stop();
    player2.stop();      
    console.log("stop pressed")
  }
  // Update state to toggle button enabled`  
  function updateSpatial() {
    setIs3D((is3D) => !is3D);
  }

  return (
    <Fragment>       
          <ButtonTL onClick={start}>Play</ButtonTL>
          <ButtonTR onClick={stop}>Stop</ButtonTR>     
          <ButtonTL2 onClick={playstereo}>Stereo</ButtonTL2>
          <ButtonTR2 onClick={playmyhifi}>3D</ButtonTR2>             
          <ButtonLL disabled={is3D} onClick={playStudio}>Studio</ButtonLL>
          <ButtonLR onClick={playCinema}>Cinema</ButtonLR>                                                          
     </Fragment>
  )
}