const {futures24hSubscribe, futures24hUnSubscribe} = useBinanceWsProvider();

useEffect(() => {
futures24hSubscribe(symbol);

return () => {

futures24hUnSubscribe(symbol);
};
}, [symbol]);

import {BinanceWsProviderProps} from “./BinanceWsProvider.props”;
import {ReadyState, useBinancePrice} from “…/…/hooks/binancePrices”;
import {createContext, useContext, useEffect, useState} from “react”;

interface BinanceWsProviderContext {

futures24hSubscribe: (symbol: string) => void;
futures24hUnSubscribe: (symbol: string) => void;
futures24hReadyState: number;

}

const defaultContextValue: BinanceWsProviderContext = {

futures24hSubscribe: (symbol: string) => {},
futures24hUnSubscribe: (symbol: string) => {},
futures24hReadyState: 0,

};

const Context = createContext(defaultContextValue);

export function useBinanceWsProvider() {
return useContext(Context);
}

const [futures24hSymbols, setFutures24hSymbols] = useState<Array<string>>([]);
const [futures24hQueue, setFutures24hQueue] = useState<Array<string>>([]);

futures24hSubscribe,
// futures24hUnSubscribe,
futures24hReadyState,

const futures24hSymbolSubscribe = (symbol: string) => {
if (futures24hSymbols.includes(symbol) || futures24hQueue.includes(symbol)) return;

setFutures24hQueue(prev => […prev, symbol]);
};

const futures24hSymbolUnsubscribe = (symbol: string) => {
if (!futures24hSymbols.includes(symbol) && !futures24hQueue.includes(symbol)) return;

setFutures24hQueue(prev => prev.filter(item => item !== symbol));
};

const subscribeFromFutures24hQueue = () => {
futures24hSubscribe(futures24hQueue);
setFutures24hSymbols([…futures24hSymbols, …futures24hQueue]);
setFutures24hQueue([]);
};

useEffect(() => {
if (futures24hQueue.length > 0 && futures24hReadyState === ReadyState.OPEN) {
subscribeFromFutures24hQueue();
}
}, [futures24hQueue]);

useEffect(() => {
if (futures24hReadyState === ReadyState.CLOSED) {
setFutures24hQueue([…futures24hSymbols, …futures24hQueue]);
setFutures24hSymbols([]);
}

if (futures24hQueue.length > 0 && futures24hReadyState === ReadyState.OPEN) {
subscribeFromFutures24hQueue();
}
}, [futures24hReadyState]);

return <Context.Provider
value={{

futures24hSubscribe: futures24hSymbolSubscribe,
futures24hUnSubscribe: futures24hSymbolUnsubscribe,
futures24hReadyState: futures24hReadyState,

}}
>
{children}
</Context.Provider>;
};

export default BinanceWsProvider;

import {useCallback, useEffect, useState} from “react”;
import {useDispatch} from “react-redux”;
import {setFutures24hBinancePriceState, setFuturesBinancePriceState, setSpotBinancePriceState} from “…/store/binancePriceSlice”;

export enum ReadyState {
UNINSTANTIATED = -1,
CONNECTING = 0,
OPEN = 1,
CLOSING = 2,
CLOSED = 3,
}

export function useBinancePrice() {

const [futures24hWs, setFutures24hWs] = useState<WebSocket | null>(null);
const [futures24hReadyState, setFutures24hReadyState] = useState(0);
const connectToWs = () => {
if (“undefined” === typeof window) {
setTimeout(“connectToWs”, 250);

return;
}


setFutures24hWs(new WebSocket(“wss://fstream.binance.com/ws”));
};

const futures24hSubscribe = useCallback((symbols: Array<string>) => {
if (null === futures24hWs || futures24hReadyState !== ReadyState.OPEN) return;
const symbolsTicker = symbols.map(symbol => ${symbol.toLowerCase()}@miniTicker);

futures24hWs.send(JSON.stringify({
id: ((new Date()).getTime() + Math.floor(Math.random() * 10000) + 10000).toFixed(0),
method: “SUBSCRIBE”,
params: symbolsTicker,
}));
}, [futures24hReadyState]);

const futures24hUnSubscribe = useCallback((symbols: Array<string>) => {
if (null === futures24hWs || futures24hReadyState !== ReadyState.OPEN) return;
const symbolsTicker = symbols.map(symbol => ${symbol.toLowerCase()}@miniTicker);

futures24hWs.send(JSON.stringify({
id: ((new Date()).getTime() + Math.floor(Math.random() * 10000) + 10000).toFixed(0),
method: “UNSUBSCRIBE”,
params: symbolsTicker,
}));
}, [futures24hReadyState]);

useEffect(() => {
if (null === futures24hWs) return;
console.log(futures24hWs);


futures24hWs.onmessage = (message: MessageEvent) => {
if (!message.data) return;

const data = JSON.parse(message.data);
const symbol = data.s;
const priceChangePercent = ((parseFloat(data.c) - parseFloat(data.o)) / parseFloat(data.o)) * 100;

dispatch(setFutures24hBinancePriceState({symbol, price: data.c, percent: priceChangePercent.toFixed(2)}));
};

futures24hWs.onopen = () => {
setFutures24hReadyState(ReadyState.OPEN);
};

futures24hWs.onclose = () => {
setFutures24hReadyState(ReadyState.CLOSED);
};
}, [futures24hWs]);


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

return {

futures24hSubscribe,
futures24hUnSubscribe,
futures24hReadyState,
};
}

import {createSlice} from “@reduxjs/toolkit”;
import {HYDRATE} from “next-redux-wrapper”;

export interface BinancePriceSlice {

futures24h: {[key: string]: {price: number; percent: number}};
}

const initialState: BinancePriceSlice = {

futures24h: {},
};

export const binancePriceSlice = createSlice({
name: “binancePrice”,
initialState,
reducers: {

setFutures24hBinancePriceState(state, action) {
const {symbol, price, percent} = action.payload;
state.futures24h[symbol] = {price, percent};
},
},
extraReducers: {
[HYDRATE]: (state, action) => {
return {
…state,
…action.payload,
};
},
},
});


export const {

setFutures24hBinancePriceState,
} = binancePriceSlice.actions;

export default binancePriceSlice.reducer;

не работает отписка от бесокетов.
Срабатывает return () => {
futures24hUnSubscribe(symbol);
}; в useEffect при демонтировании.
Но отписка UNSUBSCRIBE не происходит. Проанализируй код и найди баг, почему не происходит отписка