Пример простой web панели с "живым" обновлением.

  • Михаил
  • 8 мин. на прочтение
  • 127
  • 07 Jun 2024
  • 07 Jun 2024

Сегодня давайте рассмотрим пример реализации панели в React с использованием SignalR и Redux.

1. Создадим React-компонент для панели:

import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { updatePanelData } from './Actions';
import { HubConnectionBuilder } from '@microsoft/signalr';

const Panel = () => {
  const dispatch = useDispatch();
  const panelData = useSelector((state) => state.panelData);

  // Подключение к SignalR-хабу и обработка обновлений
  React.useEffect(() => {
    const connection = new HubConnectionBuilder().withUrl('https://localhost:5000/hub').build();

    connection.on('PanelDataUpdated', (data) => {
      dispatch(updatePanelData(data));
    });

    connection.start().catch((err) => console.error(err.toString()));

    return () => {
      connection.stop();
    };
  }, [dispatch]);

  return (
    <div className="panel">
      <h2>Panel</h2>
      <div className="panel-item">
        <label>Первое значение : </label>
        <span>{panelData.value1}</span>
      </div>
      <div className="panel-item">
        <label>Второе значение : </label>
        <span>{panelData.value2}</span>
      </div>
      <div className="panel-item">
        <label>Третье значение : </label>
        <span>{panelData.value3}</span>
      </div>
    </div>
  );
};

export default Panel;

2. Создадим Redux-экшены и редюсер:

// actions.js
export const UPDATE_PANEL_DATA = 'UPDATE_PANEL_DATA';
export const updatePanelData = (data) => ({
 type: UPDATE_PANEL_DATA,
 payload: data,
});
// reducer.js
import { UPDATE_PANEL_DATA } from './actions';
const initialState = {
 value1: 0,
 value2: 0,
 value3: 0,
};
const panelReducer = (state = initialState, action) => {
 switch (action.type) {
   case UPDATE_PANEL_DATA:
     return { ...state, ...action.payload };
   default:
     return state;
 }
};
export default panelReducer;

3. Настроим store и подключим редюсер к приложению:

// store.js
import { createStore, combineReducers } from 'redux';
import panelReducer from './reducer';
const rootReducer = combineReducers({
 panelData: panelReducer,
});
const store = createStore(rootReducer);
export default store;
// App.js
import './index.scss';
import { Provider } from 'react-redux';
import store from './Panel/Store';
import Panel from './Panel/Panel';
import SidePanel from './Panel/SidePanel';

function App() {
  return (
    <div className="App">
      <Provider store={store}>
        <Panel />
      </Provider>

      <SidePanel />
    </div>
  );
}

export default App;

4. Настроим SignalR-соединение на серверной стороне:

    public class PanelWorker : BackgroundService
    {
        private IHubContext hubContext;
        private static CancellationTokenSource _cts;
        private Random random= new Random();
        public PanelWorker(IHubContext hubContext)
        {
            this.hubContext = hubContext;
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            _cts = new CancellationTokenSource();
            try
            {
                while (!_cts.Token.IsCancellationRequested)
                {
                    var data = new PanelData()
                    {
                         Value1 = random.Next(),
                         Value2 = random.Next(),
                         Value3 = random.Next(),
                    };
                    await hubContext.Clients.All.SendAsync("PanelDataUpdated", data);
                    await Task.Delay(1000);
                }
            }
            catch (Exception _) when (_ is Exception or OperationCanceledException or TaskCanceledException)
            {
            }
            finally
            {
                _cts.Dispose();
            }
            await Task.CompletedTask;
        }
    }

    public class PanelData
    {
        public int Value1 { get; set; }
        public int Value2 { get; set; }
        public int Value3 { get; set; }
    }

В этом примере:

1. Компонент Panel использует useSelector для получения текущих значений панели из Redux-хранилища и useDispatch для отправки действия updatePanelData.
2. В useEffect устанавливается SignalR-соединение с хабом /panelHub. При получении события 'PanelDataUpdated' от сервера, действие updatePanelData отправляется в Redux-хранилище, что приводит к обновлению состояния панели и перерисовке компонента.
3. Redux-экшен updatePanelData создает объект действия с типом UPDATE_PANEL_DATA и payload, содержащим обновленные данные панели.
4. Redux-редюсер panelReducer обрабатывает действие UPDATE_PANEL_DATA, обновляя состояние панели в хранилище.

Таким образом, ваше приложение React будет отображать панель, а при получении обновлений от сервера через SignalR, значения в панели будут автоматически обновляться, используя Redux для управления состоянием.