【问题标题】:Updating state without rendering the whole React component (useState)更新状态而不渲染整个 React 组件(useState)
【发布时间】:2021-05-05 13:40:45
【问题描述】:

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

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

相比之下,我也尝试了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>
  )
}

【问题讨论】:

    标签: reactjs tone.js


    【解决方案1】:

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

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

    【讨论】:

    • 太好了,谢谢查尔斯。我将深入研究依赖方面,但您的回答为我节省了一些时间!
    • 我想重点是使用 UseState 定义 Player 类。 useEffect 的问题并不是真正的核心问题,因为这个 Hook 中的函数可以用例如触发。按钮之一。
    猜你喜欢
    • 2021-03-10
    • 1970-01-01
    • 2021-01-17
    • 2021-05-19
    • 2022-01-26
    • 1970-01-01
    • 2021-09-09
    • 2019-06-26
    • 2022-01-17
    相关资源
    最近更新 更多