Search code examples
javascriptreactjsreact-hooksreact-context

React useState in Context stay undefined even after using setState


I'm facing a problem with my React Context I'm trying to use setState but the value of the state stay undefined

Context.js

import {
  createContext,
  useContext,
  useMemo,
  useState,
  useEffect
} from "react";

const PlayerGamepadContext = createContext();

export const PlayerGamepadProvider = ({
  ...props
}) => {
  const [gamepads, setGamepads] = useState([])
  const [gamepadsCount, setGamepadsCount] = useState(0)
  const [mappedGamepads, setMappedGamepads] = useState([])

  // Triggered when button state change
  // (button: string, isPressed: bool, controllerIndex: integer) => void
  const [onButtonStateChanged, setOnButtonStateChanged] = useState(undefined)

  useEffect(() => {
    console.log("STATE CHANGED")
    console.log(setOnButtonStateChanged);
    console.log(onButtonStateChanged); // This shouldn't be undefined after I call "setOnButtonStateChanged"
  }, [onButtonStateChanged])

  const value = useMemo(() => {
    return {
      gamepads,
      mappedGamepads,
      playerCount: mappedGamepads?.length,
      setOnButtonStateChanged,
      setOnGamepadConnected
    }
  }, [
    gamepads,
    mappedGamepads,
    setOnButtonStateChanged,
    setOnGamepadConnected
  ])

  if (typeof window === 'undefined') {
  return "Loading";
}

  return <PlayerGamepadContext.Provider value={value} {...props} />
}

export const usePlayerGamepads = () => {
  const context = useContext(PlayerGamepadContext)
  return context
}

Component.jsx

import React, { useEffect } from 'react'
import { usePlayerGamepads } from '@/components/Context/PlayerGamepadContext'

const TestController = props => {
  const [state, setState] = React.useState(undefined)
  const [init, setInit] = React.useState(false)
  const playerGamepads = usePlayerGamepads()

  const handleChange = (button, isPressed, index) => {
    console.log(button, isPressed, index);
  };

  useEffect(() => {
    setState(playerGamepads)
    if (playerGamepads && !init) {
      setInit(true)
      console.log("CALL SET STATE");
      playerGamepads.setOnButtonStateChanged(handleChange)
    }
  }, [playerGamepads]);

  return (
    <div></div>
  )
}

export default TestController

And here is the console output

1- CALL SET STATE
2- undefined undefined undefined
3- STATE CHANGED
4- ƒ dispatchSetState(fiber, queue, action) {........
5- undefined

I'm a bit confused, why is my handleChange function called after using setOnButtonStateChanged (output line 2) but more importantly, why is onButtonStateChanged still undefined after changing it ?

I tried passing a function through the PlayerGamepadProvider props and it worked, but I need to be able to set it from the state.

I also tried to give onButtonStateChanged a default value

const [onButtonStateChanged, setOnButtonStateChanged] = useState(console.log)

but it directly takes the undefined value instead

Thank in advance for you help


Solution

  • You are setting the result of handleChange as OnButtonStateChanged, which is why the console log is called and the result is undefined.

    Instead of:

    playerGamepads.setOnButtonStateChanged(handleChange)
    

    Try:

    playerGamepads.setOnButtonStateChanged(() => handleChange)