All files / src/hooks useWsTrade.ts

80.64% Statements 50/62
60% Branches 15/25
87.5% Functions 7/8
83.33% Lines 50/60

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117  1x 1x   1x 1x 1x                               6x 6x 6x 6x   6x 6x   6x 2x 2x 2x 2x             6x 2x 2x         2x 2x 2x   2x 2x 2x   2x 2x             2x 2x       2x 1x 1x 1x 1x     2x         2x 2x 2x 2x     2x 2x 2x 2x   2x 2x 2x 2x 2x                               6x     1x  
import {ITrade, ImarketCodes, TROptionsInterface} from '../interfaces';
import {useRef, useState, useEffect} from 'react';
import {throttle} from 'lodash';
 
import updateQueueBuffer from '../functions/updateQueueBuffer';
import socketDataEncoder from '../functions/socketDataEncoder';
import isImarketCodes from '../functions/isImarketCodes';
 
/**
 * useWsTrade is a custom hook that connects to a WebSocket API
 * and retrieves real-time trade data for a given market code.
 * @param targetMarketCodes - Array of market codes to retrieve trade data for.
 * @param options - `throttle_time` the data update frequency(ms).
 * @param options - `max_length_queue` Maximum number of items in data buffer.
 * @throws targetMarketCodes should be React State Value, if not, unexpected errors can occur.
 * @returns Object with the WebSocket object, connection status, and real-time trade data.
 */
function useWsTrade(
  targetMarketCodes: ImarketCodes,
  onError?: (error: Error) => void,
  options: TROptionsInterface = {},
) {
  const {throttle_time = 400, max_length_queue = 100, debug = false} = options;
  const SOCKET_URL = 'wss://api.upbit.com/websocket/v1';
  const socket = useRef<WebSocket | null>(null);
  const buffer = useRef<ITrade[]>([]);
 
  const [isConnected, setIsConnected] = useState<boolean>(false);
  const [socketData, setSocketData] = useState<ITrade[]>();
 
  const throttled = throttle(() => {
    try {
      const updatedBuffer = updateQueueBuffer(buffer.current, max_length_queue);
      buffer.current = updatedBuffer;
      setSocketData(updatedBuffer);
    } catch (error) {
      console.error(error);
    }
  }, throttle_time);
 
  // socket 세팅
  useEffect(() => {
    try {
      Iif (targetMarketCodes && !isImarketCodes(targetMarketCodes)) {
        throw new Error(
          'targetMarketCodes does not have the correct interface',
        );
      }
      if ([targetMarketCodes].length > 0 && !socket.current) {
        socket.current = new WebSocket(SOCKET_URL);
        socket.current.binaryType = 'arraybuffer';
 
        const socketOpenHandler = () => {
          setIsConnected(true);
          Iif (debug)
            console.log('[completed connect] | socket Open Type: ', 'trade');
          if (socket.current?.readyState == 1) {
            const sendContent = [
              {ticket: 'test'},
              {
                type: 'trade',
                codes: [targetMarketCodes.market],
              },
            ];
            socket.current.send(JSON.stringify(sendContent));
            Iif (debug) console.log('message sending done');
          }
        };
 
        const socketCloseHandler = () => {
          setIsConnected(false);
          setSocketData([]);
          buffer.current = [];
          Iif (debug) console.log('connection closed');
        };
 
        const socketErrorHandler = (event: Event) => {
          const error = (event as ErrorEvent).error as Error;
          console.error('[Error]', error);
        };
 
        const socketMessageHandler = (evt: MessageEvent<ArrayBuffer>) => {
          const data = socketDataEncoder<ITrade>(evt.data);
          data && buffer.current.push(data);
          throttled();
        };
 
        socket.current.onopen = socketOpenHandler;
        socket.current.onclose = socketCloseHandler;
        socket.current.onerror = socketErrorHandler;
        socket.current.onmessage = socketMessageHandler;
      }
      return () => {
        if (socket.current) {
          if (socket.current.readyState != 0) {
            socket.current.close();
            socket.current = null;
          }
        }
      };
    } catch (error) {
      Iif (error instanceof Error) {
        if (onError) {
          onError(error);
        } else {
          console.error(error);
          throw error;
        }
      }
    }
  }, [targetMarketCodes]);
 
  return {socket: socket.current, isConnected, socketData};
}
 
export default useWsTrade;