const ScreenerPage: NextPage = () => {
   const {symbols} = useBinanceSymbols();
  const [activeSymbol, setActiveSymbol] = useState("BTCUSDT");
  const [symbolsFilters, setSymbolsFilters] = useState<string>("");
  const [filteredSymbols, setFilteredSymbols] = useState<string[]>(symbols.filter((symbol) => symbol.includes(symbolsFilters.toUpperCase())));
  const [connectedSymbols, setConnectedSymbols] = useState<string[]>([]);
  const {futures24hSubscribe} = useBinanceWsProvider();
  const [coinData, setCoinData] = useState<Array<{ symbol: string; price: number; percent: number }>>([]);

  console.log(coinData);

  const handleCoinData = (coin: { price: number; percent: number }, symbol: string) => {
    setCoinData(prevCoinData => {
      const existingCoinDataIndex = prevCoinData.findIndex(item => item.symbol === symbol);

      if (existingCoinDataIndex !== -1) {
        prevCoinData[existingCoinDataIndex].price = coin.price;
        prevCoinData[existingCoinDataIndex].percent = coin.percent;

        return [...prevCoinData];
      } else {
        return [...prevCoinData, {symbol, price: coin.price, percent: coin.percent}];
      }
    });
  };

  const sortByPrice = (a: string, b: string) => {
    const coinA = coinData.find(coin => coin.symbol === a);
    const coinB = coinData.find(coin => coin.symbol === b);
  
    if (coinA && coinB) {
      return coinA.price - coinB.price;
    }
  
    return 0;
  };

  const sortByPercent = (a: string, b: string) => {
    const coinA = coinData.find(coin => coin.symbol === a);
    const coinB = coinData.find(coin => coin.symbol === b);
  
    if (coinA && coinB) {
      return coinA.percent - coinB.percent;
    }
  
    return 0;
  };

  useEffect(() => {
    if(activeSymbol)
  
      futures24hSubscribe(activeSymbol);
  }, [activeSymbol]);
  return <>
 <Grid sx={{backgroundColor: "#ECECEC"}} container spacing={1} height={999} p={1}>
      <Grid item xs={1.7}>
        <Box sx={{width: "100%", height: "100%"}}>
          <Box sx={{width: "100%", height: 644, bgcolor: "#fff", overflow: "hidden", borderRadius: "8px"}}>
            <KeyboardLayoutReplacementInput ofScreener label="Поиск" value={symbolsFilters} onChange={setSymbolsFilters} />
            <Grid container >
              <Grid item xs={6} display="flex" flexDirection="column" alignItems="center">
                <IconButton sx={{py:0.1, px: 1.2, my:0, " & :hover": {"path": {fill: "#B41C18"}}}} color="error" >
                  <ArrowDropDownIcon onClick={() => setFilteredSymbols(prev => [...prev].sort((a, b) => b.localeCompare(a)))}
                    sx={{cursor: "pointer", fontSize: "9px", mr: .5, transform: "rotate(180deg)"}} /> 
                </IconButton>
                <IconButton sx={{py:0.1, px: 1.2, my:0, " & :hover": {"path": {fill: "#B41C18"}}}} color="error" >
                  <ArrowDropDownIcon onClick={() => setFilteredSymbols(prev => [...prev].sort((a, b) => a.localeCompare(b)))}
                    sx={{cursor: "pointer", fontSize: "9px", mr: .5, transform: ""}} /> 
                </IconButton>
              </Grid>
              <Grid item xs={3.5} display="flex" flexDirection="column" alignItems="center">
                <ArrowDropDownIcon
                  sx={{cursor: "pointer", " & :hover": {fill: "#B41C18"}, fontSize: "9px", mr: .5, transform: "rotate(180deg)"}}
                  onClick={() => {
                    const sortedSymbols = [...filteredSymbols].sort(sortByPrice);
                    setFilteredSymbols(sortedSymbols);
                  }}
                />
                <ArrowDropDownIcon
                  sx={{cursor: "pointer", " & :hover": {fill: "#B41C18"}, fontSize: "9px", mr: .5, transform: ""}}
                  onClick={() => {
                    const sortedSymbols = [...filteredSymbols].sort((a, b) => sortByPrice(b, a)); // Reverse sorting
                    setFilteredSymbols(sortedSymbols);
                  }}
                />
              </Grid>
              <Grid item xs={2.5} display="flex" flexDirection="column" alignItems="center">
                <ArrowDropDownIcon
                  sx={{cursor: "pointer", " & :hover": {fill: "#B41C18"}, fontSize: "9px", mr: .5, transform: "rotate(180deg)"}}
                  onClick={() => {
                    const sortedSymbols = [...filteredSymbols].sort(sortByPercent);
                    setFilteredSymbols(sortedSymbols);
                  }}
                />
                <ArrowDropDownIcon
                  sx={{cursor: "pointer", " & :hover": {fill: "#B41C18"}, fontSize: "9px", mr: .5, transform: ""}}
                  onClick={() => {
                    const sortedSymbols = [...filteredSymbols].sort((a, b) => sortByPercent(b, a)); // Reverse sorting
                    setFilteredSymbols(sortedSymbols);
                  }}
                />
              </Grid>
            </Grid>
            <Box className="scroll-block" sx={{overflowY: "scroll", overflowX: "hidden", height: 620}}>
              {filteredSymbols.map((symbol) => (
                <ScreenerSymbol
                  key={symbol}
                  symbol={symbol}
                  activeSymbol={activeSymbol}
                  setActiveSymbol={setActiveSymbol}
                  connectedSymbols={connectedSymbols}
                  setConnectedSymbols={setConnectedSymbols}
                  symbolsFilters={symbolsFilters}
                  handleCoinData={(coin: any) => handleCoinData(coin, symbol)} 
                />
              ))}
            </Box>
          </Box>
        </Box>
      </Grid>
  </>;
};

export default ScreenerPage;

import {RefObject, useState, useMemo, useEffect} from "react";

export default function useOnScreen(ref: RefObject<HTMLElement>) {
  const [isIntersecting, setIntersecting] = useState(false);

  const observer = useMemo(() => new IntersectionObserver(
    ([entry]) => setIntersecting(entry.isIntersecting)
  ), [ref]);

  useEffect(() => {
    if (null !== ref.current) {
      observer.observe(ref.current);
    }

    return () => observer.disconnect();
  }, []);

  return isIntersecting;
}


export interface ScreenerSymbolProps {
    symbol: string
    activeSymbol: string
    setActiveSymbol: (symbol: string) => void
    connectedSymbols: string[]
    setConnectedSymbols: Dispatch<SetStateAction<string[]>>
    symbolsFilters: string
    handleCoinData: any
}
const ScreenerSymbol = ({symbol, activeSymbol, setActiveSymbol, connectedSymbols, setConnectedSymbols, symbolsFilters, handleCoinData}: ScreenerSymbolProps) => {
   const symbolRef = useRef<HTMLDivElement>(null);
  const isVisible = useOnScreen(symbolRef);
  
  const coin = useSelector((state: AppState) => state.binancePrice.futures24h[symbol.toUpperCase()]);
  const {futures24hSubscribe, futures24hUnSubscribe} = useBinanceWsProvider();
    
  useEffect(() => {
    if(!coin) return;
    handleCoinData(coin);
  }, [coin, handleCoinData]);

  useEffect(() => {
    if (symbolsFilters) {
        
      if (connectedSymbols.includes(symbol)) {
        futures24hSubscribe(symbol);  
      } else {
        futures24hUnSubscribe(symbol);
        setConnectedSymbols(prev => prev.filter(item => item !== symbol));
      }
    } else {
      if (isVisible) {
        futures24hSubscribe(symbol);
          
        if(!connectedSymbols.includes(symbol)) {
          setConnectedSymbols(prev => [...prev, symbol]); 
        }   
      } else {
        futures24hUnSubscribe(symbol);
        setConnectedSymbols(prev => prev.filter(item => item !== symbol));
      }
    }
  }, [symbolsFilters, symbol, isVisible]);
  
  useEffect(() => {
      
    if (isVisible) {
      setConnectedSymbols(prev => {
        if (prev.includes(symbol)) {
          return prev.filter(item => item === symbol);
        }
  
        return [...prev, symbol]; 
      });
    } else {
      setConnectedSymbols(prev => { 
        return prev.filter(item => item !== symbol); 
      });
    }
  }, [symbolsFilters, symbol, isVisible]);
    
  useEffect(() => { 
  
    return () => futures24hUnSubscribe(symbol);
  }, []);  
  return <>
    <Grid ref={symbolRef} container sx={{cursor: "pointer", backgroundColor: activeSymbol === symbol ? "#B41C18" : ""}} onClick={() => setActiveSymbol(symbol)} borderBottom="1px solid #EEEEEE" py={1} px={1.2}>
      <Grid item xs={6.5} display="flex">
        <StarIcon sx={{fontSize: 14, mt: .3}} />
        <Typography color={activeSymbol === symbol ? "#fff" : "000000DE"} fontSize={13} fontWeight={300} ml={.3}>{symbol}</Typography>
      </Grid>
      {
        coin ? (
          <Grid item xs={5.5} display="flex" justifyContent="space-between">
            <Typography fontSize={13} color={activeSymbol === symbol ? "#fff" : ""} fontWeight={300}>${FormatPrice({amount: coin.price, currency_iso_code: "USD", dec: "decimal"})}</Typography>
            <Typography fontSize={13} color={activeSymbol === symbol ? "#fff" : coin.percent >= 0 ? "#006943" : "#B41C18"} fontWeight={300}>
              {coin.percent >= 0 ? `+${coin.percent}` : coin.percent}%
            </Typography>
          </Grid>
        ) : (
          <Grid item xs={5} display="flex" justifyContent="space-between">
            <Skeleton variant="text" width="40px" height="12px" sx={{bgcolor: "#454545"}} />
            <Skeleton variant="text" width="20px" height="12px" sx={{bgcolor: "#006943"}} />
          </Grid>)
      }
    </Grid>
  </>;
};
  
export default ScreenerSymbol;

1. в ScreenerSymbol запрашиваются const coin: {
    price: number;
    percent: number;
} по symbol из ScreenerPage 
  const {symbols} = useBinanceSymbols(); // 203 монеты.
2. Если есть coin, то он отправляется в handleCoinData
  useEffect(() => {
    if(!coin) return;
    handleCoinData(coin);
  }, [coin, handleCoinData]); и заполняет массив coinData данными
3. sortByPrice и sortByPercent 
              <Grid item xs={3.5} display="flex" flexDirection="column" alignItems="center">
                <ArrowDropDownIcon
                  sx={{cursor: "pointer", " & :hover": {fill: "#B41C18"}, fontSize: "9px", mr: .5, transform: "rotate(180deg)"}}
                  onClick={() => {
                    const sortedSymbols = [...filteredSymbols].sort(sortByPrice);
                    setFilteredSymbols(sortedSymbols);
                  }}
                />
                <ArrowDropDownIcon
                  sx={{cursor: "pointer", " & :hover": {fill: "#B41C18"}, fontSize: "9px", mr: .5, transform: ""}}
                  onClick={() => {
                    const sortedSymbols = [...filteredSymbols].sort((a, b) => sortByPrice(b, a)); // Reverse sorting
                    setFilteredSymbols(sortedSymbols);
                  }}
                />
              </Grid>
              <Grid item xs={2.5} display="flex" flexDirection="column" alignItems="center">
                <ArrowDropDownIcon
                  sx={{cursor: "pointer", " & :hover": {fill: "#B41C18"}, fontSize: "9px", mr: .5, transform: "rotate(180deg)"}}
                  onClick={() => {
                    const sortedSymbols = [...filteredSymbols].sort(sortByPercent);
                    setFilteredSymbols(sortedSymbols);
                  }}
                />
                <ArrowDropDownIcon
                  sx={{cursor: "pointer", " & :hover": {fill: "#B41C18"}, fontSize: "9px", mr: .5, transform: ""}}
                  onClick={() => {
                    const sortedSymbols = [...filteredSymbols].sort((a, b) => sortByPercent(b, a)); // Reverse sorting
                    setFilteredSymbols(sortedSymbols);
                  }}
                />
              </Grid>

сортируют все монеты по имеющимся данным price и percent.
3. Сортировка происходит некорректно и я не могу понять почему. 
Проанализируй код и найди как сделать сортировку правильной.
-- Сортировка сама происходит правильно, но он сортирует не все монеты, а примерно в блоке видимости. может быть неправиьно происходит проверка тут  const sortByPrice = (a: string, b: string) => {

    const coinA = coinData.find(coin => coin.symbol === a);
    const coinB = coinData.find(coin => coin.symbol === b);
    console.log(coinA);
    console.log(coinB);
  
    if (coinA && coinB) {
      return coinB.price - coinA.price; 
    }
  
    return 0;
  };
, но может и нет, я не пойму. Проанализируй код и пойми почему неправильно происходит сортировка по всем coinData