【问题标题】:TypeScript SignalR base implementationTypeScript SignalR 基础实现
【发布时间】:2022-12-11 16:47:17
【问题描述】:

我刚开始使用 SignalR,目前已成功连接我的应用程序到 C# 后端。

但是是否有任何好的教程/repos/helper 库已经建立了一个很好的基础实现?

当它将在许多地方使用并且可能以一种很好的离散和可控方式处理事件时,最好摆脱它。

   const connection = new HubConnectionBuilder()
      .withUrl(url, options)
      .withAutomaticReconnect()
      .withHubProtocol(new JsonHubProtocol())
      .configureLogging(LogLevel.Information)
      .build()

    useEffect(() => {
       const setUpSignalR = () => {
          await connection.start()
          .....
       
       }
       setUpSignalR()
       
       return () => {
          connection.stop()
       }
     }, [])

【问题讨论】:

    标签: reactjs typescript signalr


    【解决方案1】:

    对于任何感兴趣的人来说,这就是一切的结局。

    如果还不错,请随时提出改进建议或自己使用。

    import { HubConnection } from '@microsoft/signalr'
    
    export type ConnectionState = {
      error?: Error
      loading: boolean
      isConnected: boolean
      accessToken?: string
      connection?: HubConnection
    }
    
    export const initialConnectionState = {
      error: undefined,
      loading: true,
      isConnected: false,
      accessToken: undefined,
      connection: undefined,
    }
    
    
    import { createContext, useContext } from 'react'
    import { ConnectionState, initialConnectionState } from './state'
    
    export const SignalRContext = createContext<ConnectionState>(initialConnectionState)
    const useSignalRContext = () => {
      const context = useContext(SignalRContext)
      if (!context) throw new Error('There is no context values for signalr')
      return context
    }
    export default useSignalRContext
    
    
    import { SignalRContext } from './SignalRContext'
    import { useConnection } from './useConnection'
    
    const SignalRWrapper = ({ children }) => {
      const connection = useConnection()
      return <SignalRContext.Provider value={connection}>{children}</SignalRContext.Provider>
    }
    export default SignalRWrapper
    
    
    
    import { useEffect, useReducer, useRef } from 'react'
    import { useConfig } from '@griegconnect/krakentools-react-kraken-app'
    import { HubConnectionBuilder, HubConnectionState, JsonHubProtocol, LogLevel } from '@microsoft/signalr'
    import { useTenantServices } from '../../api-services/plan/TenantServices/TenantServices'
    import { ConnectionState, initialConnectionState } from './state'
    import { log } from './utils'
    const startSignalRConnection = async (connection) => {
      try {
        await connection.start()
        log('SignalR connection established')
      } catch (err) {
        log('SignalR Connection Error: ', err)
        setTimeout(() => startSignalRConnection(connection), 5000)
      }
    }
    export const useConnection = (): ConnectionState => {
      const config = useConfig()
      const { enlistClient, delistClient } = useTenantServices()
      const reducer = (state: ConnectionState, newState: ConnectionState): ConnectionState => ({ ...state, ...newState })
      const [state, setState] = useReducer(reducer, initialConnectionState)
      const componentMounted = useRef(true)
      useEffect(() => {
        return () => {
          componentMounted.current = false
        }
      }, [])
      useEffect(() => {
        const connection = new HubConnectionBuilder()
          .withUrl(`${config.api.planApiUrl}/planhub`)
          .withAutomaticReconnect()
          .withHubProtocol(new JsonHubProtocol())
          .configureLogging(LogLevel.Information)
          .build()
        startSignalRConnection(connection).then(() => {
          if (componentMounted.current) setState({ loading: false, isConnected: true, connection })
          enlistClient(connection.connectionId)
        })
        connection.onclose(() => {
          log('SignalR connection closed')
          delistClient(connection.connectionId)
        })
        connection.onreconnected(() => {
          log('SignalR connection reconnecting')
          enlistClient(connection.connectionId)
        })
        return () => {
          connection.stop()
        }
      }, [config.api.planApiUrl, delistClient, enlistClient])
      return state
    }
    
    

    这是后端实现

    {
        public class PlanHub : Hub, IPlanHub
        {
            private readonly IHubContext<PlanHub> hubContext;
            public PlanHub(IHubContext<PlanHub> hubContext)
            {
                this.hubContext = hubContext;
            }
    
            public async Task AddToGroupAsync(string companyTenantId, string connectionId, CancellationToken cancel)
            {
                await hubContext.Groups.AddToGroupAsync(connectionId, companyTenantId, cancel);
            }
    
            public async Task RemoveFromGroupAsync(string companyTenantId, string connectionId, CancellationToken cancel)
            {
                await hubContext.Groups.AddToGroupAsync(connectionId, companyTenantId, cancel);
            }
        }
    }
    

    与一堆不同的事件处理程序一起。

        public class TasksEventHandler : ITasksEventHandler
        {
            private readonly IHubContext<PlanHub> hubContext;
    
            public TasksEventHandler(IHubContext<PlanHub> hubContext)
            {
                this.hubContext = hubContext;
            }
            
            public async Task HandleSaveTask(string companyTenantId, IList<TaskDetailDto> tasks, CancellationToken cancel)
            {
                await hubContext.Clients.Group(companyTenantId).SendAsync("saveTask", tasks, cancel);
            }
    
            .......
    
    
        }
    }
    

    【讨论】: