Не рендерится полученая data, который получил из socket.io | ReactJS / NodeJS

Есть приложение, клиент на React, а сервер на node js. Ситуация такая: Я получаю данные с mysql (файл: matches.js) и передаю их на клиент с помощью socket-io. Со стороны клиента пишу конфиг socket.io-client(файл: HotBoard.jsx). Но рендер (табличка с live матчами) не отображается. Подскажите и объясние что же не так?

Так же приветствуется критика по коду, желательно чтобы был некий рефакторинг. Нужно socket io в node, был в отдельном файле, т.к в дальнейшем много чего будет отправлятся по сокету.

Ниже указал исходники файлов:

  • app.js (main),
  • matches.js (контроллер),
  • matches.js (роутер),
  • HotBoard.jsx (client React)

app.js (входной файл):

    require('dotenv').config();
    const express = require('express');
    const PORT = process.env.PORT || 8080;
    const axios = require('axios');
    const bodyParser = require('body-parser');
    const cors = require('cors');
    
    const matchesRoutes = require('./routes/matches.js');
    const app = express();
    const server = app.listen(PORT, (err) => {
        if(err) return err;
        console.log(`Server on ${PORT} is running...`);
    });
    
    app.use(bodyParser.urlencoded({extended: true}));
    app.use(cors());
    app.use(express.json());
    app.use('/matches', matchesRoutes);
    
    module.exports = app;

matches.js (./controller):

    const db = require('../db.js');
    const app = require('../app');
    const http = require('http');
    const server = http.createServer(app);
    
    const socketIO = require('socket.io')(server, {
        cors: {
            origin: 'http://localhost:3000'
        }
    })
    
    module.exports.live = (req, res) => {
        db.query('SELECT * FROM livematches', ((err, result) => {
            if(err) throw err;
    
            socketIO.emit('livematches', result);
            // res.send(result);
        }));
    }
    
    server.listen(8081, () => {
        console.log('Socket.io listening on 8081');
      });

Хотел сделать на одном порту, как и в основном - входном файле app.js (:8080), но не получалось, да и в статьях прочитал, что сокеты нужно использовать на другом порте.

matches.js (./routes):

    const express = require('express');
    const router = express.Router();
    const controller = require('../controllers/matches.js');
    
    router.get('/live', controller.live);
    
    module.exports = router;

HotBoard.jsx:

    import React, { useEffect, useState } from 'react';
import './HotBoard.css';
import { Swiper, SwiperSlide } from "swiper/react";
import SwiperCore, { Autoplay } from 'swiper';
import axios from 'axios';
import Tippy from '@tippyjs/react';
import 'tippy.js/dist/tippy.css';

import friendly from '../../assets/ico/friendly.webp';
import notRecogLeague from '../../assets/ico/notRecogLeague.webp';
import undefTeam from '../../assets/ico/undefTeam.webp';
import wcLogo from '../../assets/ico/wcLogo.webp';
import io from 'socket.io-client';

SwiperCore.use([Autoplay]);

const socket = io('http://localhost:8081');

function HotBoard() {
    const[liveMatches, setLiveMatches] = useState();
    const[matchesQuant, setMatchesQuant] = useState('0');

    useEffect(() => {
        const fetchData = async () => {
            socket.on('livematches', data => {
                setLiveMatches(data && data.map((e, i) => {
                    return  <SwiperSlide key={'live' + i}>
                                <div className='slideWrap'>
                                    <progress value={e.time.includes('Доп.') ? String(e.time).split('. ')[1].replace('\'', '') :  String(e.time).replace('\'', '') && e.time === 'Перерыв' ? '45' : String(e.time).replace('\'', '')} max={e.time.includes('Доп.') ? '120' : '90'}></progress>
                                    <span className="hName">{e.hName.slice(0, 10)}</span>
                                    <Tippy placement='bottom' content={e.hName}>
                                        <img width={'13px'} src={e.hLogo === null ? undefTeam : e.hLogo} alt={e.hName} />
                                    </Tippy>
                                    <span className='hScore'>{e.hScore}</span>
                                    <div className="lLogoTime">
                                        <Tippy placement='bottom' content={e.lName + ' | ' + String(e.round).replace('null', '') + ', ' + String(e.roundInfo).replace('null', '')}>
                                            <img width={'14px'} src={e.lLogo === 'https://s.scr365.net/s1/logo/13_36_14/fPHr8_16_439.png' ? friendly : e.lLogo && e.lLogo === 'https://s.scr365.net/img/ball16.png' ? notRecogLeague : e.lLogo && e.lLogo === 'https://s.scr365.net/s1/logo/12_250_17/a7wHB_16_438.png' ? friendly : e.lLogo && e.lLogo === 'https://s.scr365.net/s1/logo/22_33_11/46atU_16_742.png' ? wcLogo : e.lLogo} alt={e.lName} />
                                        </Tippy>
                                        <span className="time">{e.time === 'Перерыв' ? 'Пер.' : e.time && e.time.includes('Доп.') ? `${'ДВ' + e.time.split('.')[1]}` : e.time}</span>
                                    </div>
                                    <span className='aScore'>{e.aScore}</span>
                                    <Tippy placement='bottom' content={e.aName}>
                                        <img width={'13px'} src={e.aLogo === null ? undefTeam : e.aLogo} alt={e.aName} />
                                    </Tippy>
                                    <span className="aName">{e.aName.slice(0, 10)}</span>
                                </div>
                            </SwiperSlide>
                })); 
                setMatchesQuant(data.length);
                if(data.length > 0) document.querySelector('.hotBoard .liveWrap span').style.color = 'red';
            });
        }

        fetchData();
    }, []);

    return (
        <div className="hotBoard">
            <Swiper centeredSlides={true} grabCursor={true} speed={5000} autoplay={{delay: 1, disableOnInteraction: false}} slidesPerView={2} breakpoints={{280: {slidesPerView: 1}, 540: {slidesPerView: 2}, 768: {slidesPerView: 3}, 1200: {slidesPerView: 5},  1920: {slidesPerView: 6}}} freeMode={true}>
                {liveMatches}
            </Swiper>
            <div className="liveWrap">
                <Tippy placement='bottom' content='Количество матчей'>
                    <span>{matchesQuant}</span>
                </Tippy>
            </div>
        </div>
    );
}

export default HotBoard;

Ответы (0 шт):