Search code examples
javascriptreactjsmobileframerjs

Device orientation data and fetching external API json data seems clashing. How can I fix it?


I am trying to get the device orientation data and fetch the API data using react hooks, but it seems like clashing.

If I could get the device orientation data, then I couldn't get the API data(I think when I could get the device orientation data, the server wasn't running), and when the server was running, I couldn't get the device orientation data. Since the server is sending data in HTTP, not https, so I tried it on the local server, but still didn't work. Seems like there is a problem in using react hooks and useEffect, but not sure.

const [alpha, setAlpha] = React.useState(0.0)

React.useEffect(() => {
    const handleOrientation = e => setAlpha(e.alpha)
    window.addEventListener("deviceorientation", handleOrientation, true)

    return () => {
        window.removeEventListener("deviceorientation", handleOrientation)
        }
}, [])

const [centerX, setCenterX] = React.useState(0.0)
const [centerY, setCenterY] = React.useState(0.0)
const [deviceX1, setDeviceX1] = React.useState(0.0)
const [deviceY1, setDeviceY1] = React.useState(0.0)
const [deviceX2, setDeviceX2] = React.useState(0.0)
const [deviceY2, setDeviceY2] = React.useState(0.0)

const [timer, setTimer] = React.useState(null)
const [isMounted, setIsMounted] = React.useState(false)

async function updateDevicePosition() {
    try {
        const result = await fetch("http://192.168.10.233:34599/")
        const data = await result.json()
        setCenterX(data[0].x)
        setCenterY(data[0].y)
        setDeviceX1(data[1].x)
        setDeviceY1(data[1].y)
        setDeviceX2(data[2].x)
        setDeviceY2(data[2].y)
    } catch (e) {
        console.error(e)
    }
    clearTimeout(timer)
    setTimer(setTimeout(updateDevicePosition, 200))
}

React.useEffect(() => {
    if (!isMounted) {
        updateDevicePosition()
        setIsMounted(true)
    }
})

After getting all the data, I hope to calculate the device position and orientation.


Solution

  • I think you're really close, but I'd do it a bit differently. First, I'd have the entire position as a single state:

    const [devicePosition, setDevicePosition] = React.useState({
      centerX: 0.0,
      centerY: 0.0,
      deviceX1: 0.0,
      deviceY1: 0.0,
      deviceX2: 0.0,
      deviceY2: 0.0,
    })
    

    Second, I'd remove the isMounted component from the state. You don't want to use state to determine if you should be updating the state. You could update the isMounted on an unmounted component.

    Third, I'd remove the useEffect that's getting called on every render. It's very unlikely that you need to make the rest call on every single render. Based on the code I think you want to make the call on the first render and then set a timer.

    Below illustrates those changes.

    const [alpha, setAlpha] = React.useState(0.0)
    const [devicePosition, setDevicePosition] = React.useState({
      centerX: 0.0,
      centerY: 0.0,
      deviceX1: 0.0,
      deviceY1: 0.0,
      deviceX2: 0.0,
      deviceY2: 0.0,
    })
    const [timer, setTimer] = React.useState(null)
    
    let isMounted = true
    React.useEffect(() => {
        const handleOrientation = e => setAlpha(e.alpha)
        window.addEventListener("deviceorientation", handleOrientation, true)
        updateDevicePosition() //make rest call on mounting
    
        return () => {
            window.removeEventListener("deviceorientation", handleOrientation)
            isMounted = false
        }
    }, [])
    
    async function updateDevicePosition() {
        try {
            const result = await fetch("http://192.168.10.233:34599/")
            const data = await result.json()
            if (isMounted) { //so you aren't setting state on an unmounted component
              setDevicePosition({
                centerX: data[0].x,
                centerY: data[0].y,
                deviceX1: data[1].x,
                deviceY1: data[1].y,
                deviceX2: data[2].x,
                deviceY2: data[2].y,
              })
           }
        } catch (e) {
            console.error(e)
        }
        if (isMounted) {
          clearTimeout(timer)
          setTimer(setTimeout(updateDevicePosition, 200))
        }
    }