Defgun
Defgun

Reputation: 79

useEffect is not firing consistently

The component is creating a positionalAudio within a 3D-scene and the audio object is tweakable by using leva. The object is being created just as it should and tweaking the position and rotation just works fine. What is not working properly is changing the volume. In the leva UI I can drag the handle of the volume an change it but no effect is taking place (I'm assuming it's because the useEffect is firing before the handle is released and effectivly no change in the value has been taking place yet. At least a console log is shown before I release the handle). When I put the new value inside the input field an press enter useEffect is firing and the volume is changing. But it works only this time and afterwards not anymore.

import * as THREE from 'three'
import React, { Suspense, useRef, useEffect, useState } from 'react'
import { useThree, useLoader } from '@react-three/fiber'
import { PositionalAudioHelper } from 'three/examples/jsm/helpers/PositionalAudioHelper.js'
import { useControls } from 'leva'

function Sound({ url, volume }) {
    const sound = useRef()
    const { camera } = useThree()
    const [listener] = useState(() => new THREE.AudioListener())
    const buffer = useLoader(THREE.AudioLoader, url)
    useEffect(() => {
        sound.current.setBuffer(buffer)
        sound.current.setRefDistance(1)
        sound.current.setRolloffFactor(0)
        sound.current.setDirectionalCone(180, 230, 0.1)
        sound.current.setLoop(true)
        sound.current.play()
        const helper = new PositionalAudioHelper(sound.current)
        sound.current.add(helper)
        camera.add(listener)
        return () => camera.remove(listener)
    }, [])
    useEffect(() => {
      sound.current.setVolume(volume)
  }, [sound.current])
    return (
        <positionalAudio ref={sound} args={[listener]} />
    )
}

export default function App({ url, position}) {
    const { posXRight, posYRight, posZRight, rotationYRight, volumeRight } = useControls({
        posXRight: {
          value: position[0],
          min: -20,
          max: 20,
          step: 1
        },
        posYRight: {
            value: position[1],
            min: -20,
            max: 20,
            step: 1
          },
        posZRight: {
            value: position[2],
            min: -20,
            max: 20,
            step: 1
        },
        rotationYRight: {
            value: 0,
            min: 0,
            max: Math.PI * 2,
            step: Math.PI * 0.25
        },
        volumeRight: {
            value: 1,
            min: 0,
            max: 1,
            step: 0.05
        }
      })
  return (
      <Suspense fallback={null}>
        <mesh position={[posXRight, posYRight, posZRight]} rotation={[0, rotationYRight, 0]}>
          <sphereGeometry args={[0.1, 32, 32]} />
          <meshStandardMaterial color="white" />
          <Sound url={url} volume={volumeRight} />
        </mesh>
      </Suspense>
  )
}

Upvotes: 0

Views: 519

Answers (1)

UjinT34
UjinT34

Reputation: 4987

useEffect(() => {
    sound.current.setVolume(volume)
}, [sound.current])

this hook is fired on initial render and every time sound.current changes (which is probably never). You should change it to [volume]

Upvotes: 1

Related Questions