javascript 从wavesurfer.js中显示currentTime会在React中触发太多的重新渲染

qyuhtwio  于 2023-03-21  发布在  Java
关注(0)|答案(3)|浏览(150)

我正在使用这个wavesurfer.js包与React来显示音频的波形。我想在他们的docs中使用这个getCurrentTime()方法来显示当前的运行时间,并在github上回答。
我得到了以秒为单位的运行时间,并更新了我的React状态以显示它。然而,它更新得非常频繁,每秒超过10次,这会触发我的组件多次重新渲染。我想知道是否有一种方法可以让它只在每秒钟通过时触发状态更改?

export function Component(){
    const [currentTime, setCurrentTime] = useState(0)
    const wavesurferRef = useRef<any>(null)

    useEffect(()=>{
        if (wavesurferRef.current){
            wavesurferRef.current.on('audioprocess', function () {

                //ISSUE: THIS MAKE COMPONENT RE-RENDERING TOO MANY TIME
                setCurrentTime(wavesurferRef.current.getCurrentTime())
            })
        }
    },[])
    

    return(
        <div ref={wavesurferRef}>
        </div>
    )
}
vpfxa7rd

vpfxa7rd1#

我可能会建议你使用useRef和一个DOM节点以及类似requestAnimationFrame的东西来更新文本。这里有一个最小的,可验证的例子。运行下面的程序,注意只有当你点击按钮并更新状态时才会发生渲染。时间戳是使用动画而不是状态转换来更新的-

const mockWavesurfer = { offset: Date.now(), getCurrentTime() { return Date.now() - this.offset } }

function useAnimationFrame(callback) {
  React.useEffect(
    () => {
      let frame
      function onFrame(frameId) {
        callback(frameId)
        frame = requestAnimationFrame(onFrame)
      }
      frame = requestAnimationFrame(onFrame)
      return () => { window.cancelAnimationFrame(frame) }
    },
    [callback]
  )
}

function App() {
  const [count, setCount] = React.useState(0)
  const wavesurferRef = React.useRef(mockWavesurfer)
  const dateRef = React.useRef()
  React.useEffect(() => console.log("render", count), [count])
  useAnimationFrame(() => {
    dateRef.current.textContent = wavesurferRef.current.getCurrentTime()
  })
  return <div>
    <button onClick={e => setCount(count + 1)} children={count} />
    <time ref={dateRef}>0</time>
  </div>
}

ReactDOM.createRoot(document.querySelector("#app")).render(<App />)
body::before { content: "render only triggers when state changes" }
button::before { content: "click:" }
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="app"></div>
e4yzc0pl

e4yzc0pl2#

一般的答案是你不需要在每个事件上调用setCurrentTime
更实际的是,你可以使用一些自定义逻辑来定义要跳过哪些事件。或者你可以使用通用的帮助函数来这样做。如果你希望你的函数被调用的频率不超过每N毫秒,那么就调用throttle。你可以使用lodash或其他实用程序库来实现这一点
https://lodash.com/docs/4.17.15#throttle
示例https://laracasts.com/series/build-modern-laravel-apps-using-inertia-js/episodes/22

useEffect(()=>{
        if (wavesurferRef.current){
            wavesurferRef.current.on('audioprocess', throttle(function () {

                //ISSUE: THIS MAKE COMPONENT RE-RENDERING TOO MANY TIME
                setCurrentTime(wavesurferRef.current.getCurrentTime())
            })
        }, 1000); // fire only once a second
    },[])
cx6n0qe3

cx6n0qe33#

因为在我的应用程序中,我只需要以秒为单位的值,所以我使用这个简单的方法来Math.floor值和setState(),因为setState只在值不同时才改变状态,因此,组件将每秒钟重新渲染一次。

useEffect(()=>{
   if (wavesurferRef.current){
       wavesurferRef.current.on('audioprocess', function () {
           setCurrentTime(Math.floor(wavesurferRef.current.getCurrentTime()))
       })
   }
},[])

相关问题