首页 > 解决方案 > 更新状态而不渲染整个 React 组件(useState)

问题描述

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

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

相比之下,我也尝试了useRef 钩子,它允许在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>
  )
}

标签: reactjstone.js

解决方案


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

其次,useEffect(()=>console.log(hi))没有任何依赖关系,因此它将在每次渲染时运行。 useEffect(()=>console.log(hi),[])只会在第一次渲染时执行。 useEffect(()=>console.log(hi),[player1])将在 player1 更改时执行。 useEffect(()=>console.log(hi),[player1, player2])将在 player1 OR 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>
  )
}

推荐阅读