Array list stored in state is empty when accessed from the provider component in which it is defined

53 views Asked by At

I have recently come across the Gamepad API and decided to try it out in a React app. It all started from that but I soon realised how limited the API is, so I thought to build on top it (or at least try). I created a provider in which I am storing "device" objects in state. I am creating "devices" from "gamepads" once a gamepad event is fired. The device class only adds a few more fields to the gamepad ones and convenience methods (nothing fancy, just trying to learn typescript and react). The problem I am having however is with filtering the list of devices from the provider itself. Accessing the list from the consumers of the context seems to work fine, but not in the Provider. In the code below, the getDeviceByGamepadIdD() method always returns undefined, despite having items in the list (which I can see rendered in the actual components). I'd appreciate if someone could help me figure out what I am doing wrong. Thanks in advance.

import { ReactNode, createContext, useEffect, useState } from "react";
import Device from "./Device";
import { getEncodedNames } from "./utils/EncodedNames";
import { EncodedName, TDevicesContext } from "./utils/types/DeviceTypes";

const DevicesContext = createContext(null as unknown as TDevicesContext);

const DevicesProvider = ({ children }: { children: ReactNode }) => {
  const [devices, setDevices] = useState<Device[]>([]);
  const [selected, setSelected] = useState<string>("");
  const [onTest, setOnTest] = useState<boolean>(false);
  const [availableNames, setAvailableNames] = useState<EncodedName[]>(
    getEncodedNames()
  );

  // THIS!!!!!!!!!
  const getDeviceByGamepadID = (id: string) => {
    const found = devices.find((device) => device.getGamepadId() === id);
    console.log("FOUND::: ", found); // this always logs "undefined"
    return found;
  };

  const addDevice = (gamepad: Gamepad) => {
    const newDevice = new Device(gamepad);
    newDevice.setConnected(true);
    setDevices((prevDevices) => [...prevDevices, newDevice]);
  };

  const removeDevice = (id: string) => {
    setDevices((prevDevices) =>
      prevDevices.filter((device) => device.getId() !== id)
    );
  };

  const updateIsConnected = (id: string, value: boolean) => {
    setDevices((prevDevices) =>
      prevDevices.map((device) => {
        if (device.getId() === id) {
          device.setConnected(value);
          return device;
        }
        return device;
      })
    );
  };

  const onDeviceConnected = (e: GamepadEvent) => {
    const device = getDeviceByGamepadID(e.gamepad.id);
    if (!device) {
      addDevice(e.gamepad);
      return;
    }

    updateIsConnected(device.getId(), true);
  };

  const onDeviceDisconnected = (e: GamepadEvent) => {
    const device = getDeviceByGamepadID(e.gamepad.id);
    if (device) {
      updateIsConnected(device.getId(), false);
    }
  };

  useEffect(() => {
    window.addEventListener("gamepadconnected", onDeviceConnected);
    window.addEventListener("gamepaddisconnected", onDeviceDisconnected);

    return () => {
      window.removeEventListener("gamepadconnected", onDeviceConnected);
      window.removeEventListener("gamepaddisconnected", onDeviceDisconnected);
    };
  }, []);

  return (
    <DevicesContext.Provider
      value={{
        devices,
        setDevices,
        selected,
        setSelected,
        onTest,
        setOnTest,
        availableNames,
        setAvailableNames,
      }}
    >
      {children}
    </DevicesContext.Provider>
  );
};

export { DevicesContext, DevicesProvider };

I can confirm that the getGamepadId() method on the Device class returns a string. I have tried exporting the getDeviceByGamepadID() to the context and use it elsewhere, and in this case it works but I'd need this to work within the provider itself. I noticed that the devices list is always empty when the method runs after being invoked by either of the event handlers, however the list does contain data when access from elsewhere. I am sure there is something about React that I haven't quite understood yet, but I can't figure out what

0

There are 0 answers