useAnimationFrame

const useAnimationFrame = (fn: () => void) => {
  useEffect(() => {
    let currentFrame: number = 0;

    const step = () => {
      fn();
      currentFrame = window.requestAnimationFrame(step);
    }

    step();

    return () => {
      window.cancelAnimationFrame(currentFrame);
    }
  }, [fn]);
}

useSocketIOClient

const socket = openSocket(getEnvVariable('API_URL'));
export const useSocketIOClient = () => socket;

useSocketIOReceiver


import openSocket from 'socket.io-client';
import { getEnvVariable } from '@utils/envs';

interface Events {
  loadRealtime: components['schemas']['RouteDto'][];
  pointFromClient: components['schemas']['RealtimeGpsSegmentDto'];
  newPostUploaded: undefined;
}

export const useSocketIOReceiver = <EventType extends keyof Events, Result extends Events[EventType]>(event: EventType, callback: (data: Result) => void) => {
  const socket = useSocketIOClient();

  useEffect(() => {
    socket.on(event, callback as any);

    return () => {
      socket.off(event, callback as any)
    }
  }, [callback, event, socket]);
}

useSocketIOSender

import { useCallback } from 'react';
import { useSocketIOClient } from '@hooks/use-socket-io-client';

interface Events {
  join: {
    username: string;
    activityId: number;
  },
  leave: string,
}

export const useSocketIOSender = <EventType extends keyof Events, Request extends Events[EventType]>(event: EventType) => {
  const socket = useSocketIOClient();

  const emit = useCallback((data: Request) => {
    socket.emit(event, JSON.stringify(data));
  }, [event, socket]);

  return { emit };
};

useTimer

Таймер реального времени с паузой и возможностью менять скорость перемещения по времени

import { useCallback, useRef, useState } from 'react';
import { useMount } from 'react-use';

export const useTimer = (initialTime, initialSpeed = 1) => {
  const relativeTime = useRef(initialTime);
  const timerStart = useRef(0);
  const timerSpeed = useRef(initialSpeed);
  const timerIsPaused = useRef(false);
  const [timerIsPausedState, setTimerIsPausedState] = useState(false);
  const lastPausedTimestamp = useRef(0);

  const refreshTimerStart = useCallback(() => timerStart.current = Date.now(), []);

  useEffect(() => {
    refreshTimerStart();
  }, [refreshTimerStart]);

  const setTime = useCallback((time: number) => {
    refreshTimerStart();
    relativeTime.current = time;
    lastPausedTimestamp.current = time;
  }, [refreshTimerStart]);

  const getTime = useCallback(() => {
    return relativeTime.current + (Date.now() - timerStart.current) * timerSpeed.current;
  }, []);

  const setSpeed = useCallback((speed: number) => {
    relativeTime.current = getTime();
    refreshTimerStart();
    timerSpeed.current = speed;
  }, [getTime, refreshTimerStart]);

  const pause = useCallback(() => {
    if (!timerIsPaused.current) {
      timerIsPaused.current = true;
      setTimerIsPausedState(true);
      lastPausedTimestamp.current = getTime();
    }
  }, [getTime]);

  const play = useCallback(() => {
    if (timerIsPaused.current) {
      timerIsPaused.current = false;
      setTimerIsPausedState(false);
      setTime(lastPausedTimestamp.current);
    }
  }, [setTime]);

  const getTimeWithPause = useCallback(() => {
    if (timerIsPaused.current) return lastPausedTimestamp.current;
    return getTime();
  }, [getTime]);

  const togglePause = useCallback((makePause?: boolean) => {
    if (makePause === true) {
      pause();
    } else if (makePause === false) {
      play();
    } else {
      if (timerIsPaused.current) {
        play();
      } else {
        pause();
      }
    }
  }, [pause, play]);

  return { getTime: getTimeWithPause, setTime, setSpeed, togglePause, paused: timerIsPausedState };
};