Eine Minderheit, aber immer noch eine große Anzahl von Projekten erfordert die Integration von Web-Sockets, um eine sofortige Reaktion einer Schnittstelle auf Änderungen zu ermöglichen, ohne dass Daten erneut abgerufen werden müssen. Es ist eine wesentliche Sache, und wir werden nicht darüber sprechen oder einen Vergleich zwischen Bibliotheken von Drittanbietern anstellen, die APIs für ein besseres Entwicklungserlebnis bereitstellen. Mein Ziel ist es zu zeigen, wie man schnell in NextJs integrieren kann. Und wie wir die Probleme lösen können, mit denen wir während der Entwicklung konfrontiert waren. @microsoft/signalr Ich hoffe, dass jeder das NextJS-Projekt bereits lokal installiert und bereitgestellt hat. In meinem Fall ist die Version . Fügen wir einige weitere wichtige Bibliotheken hinzu: (Version ) für den Datenabruf und die weitere Arbeit mit dem lokalen Cache und (Version ) – API für Web-Sockets. 13.2.4 swr 2.1.5 @microsoft/signalr 7.0.5 npm install --save @microsoft/signalr swr Beginnen wir mit der Erstellung einer einfachen und eines neuen Hooks namens , um Anfangsdaten von unserer REST-API abzurufen. Es gibt eine Liste der Nachrichten für den Chat, Felder zur Erkennung von Fehlern und des Ladestatus sowie die Methode zurück, mit der zwischengespeicherte Daten geändert werden können. fetcher useChatData mutate // hooks/useChatData.ts import useSWR from 'swr'; type Message = { content: string; createdAt: Date; id: string; }; async function fetcher<TResponse>(url: string, config: RequestInit): Promise<TResponse> { const response = await fetch(url, config); if (!response.ok) { throw response; } return await response.json(); } export const useChatData = () => { const { data, error, isLoading, mutate } = useSWR<Message[]>('OUR_API_URL', fetcher); return { data: data || [], isLoading, isError: error, mutate, }; }; Um zu testen, ob es wie vorgesehen funktioniert, aktualisieren wir unsere Seitenkomponente. Importieren Sie unseren Hook oben und extrahieren Sie Daten daraus, wie im Snippet unten. Wenn es funktioniert, werden die gerenderten Daten angezeigt. Wie Sie sehen, ist es ganz einfach. // pages/chat.ts import { useChatData } from 'hooks/useChatData'; const Chat: NextPage = () => { const { data } = useChatData(); return ( <div> {data.map(item => ( <div key={item.id}>{item.content}</div> ))} </div> ); }; Im nächsten Schritt müssen wir unsere zukünftige Seite mit Web-Sockets verbinden, Ereignisse abfangen und einen Cache mit einer neuen Nachricht aktualisieren. Ich schlage vor, mit dem Aufbau des Socket-Dienstes in einer separaten Datei zu beginnen. NewMessage Gemäß den Beispielen in den SignalR-Dokumenten müssen wir eine Verbindungsinstanz für das weitere Abhören von Ereignissen erstellen. Ich habe außerdem ein Verbindungsobjekt zum Verhindern von Duplikaten und zwei Helfer zum Starten/Stoppen der Verbindungen hinzugefügt. // api/socket.ts import { HubConnection, HubConnectionBuilder, LogLevel } from '@microsoft/signalr'; let connections = {} as { [key: string]: { type: string; connection: HubConnection; started: boolean } }; function createConnection(messageType: string) { const connectionObj = connections[messageType]; if (!connectionObj) { console.log('SOCKET: Registering on server events ', messageType); const connection = new HubConnectionBuilder() .withUrl('API_URL', { logger: LogLevel.Information, withCredentials: false, }) .withAutomaticReconnect() .build(); connections[messageType] = { type: messageType, connection: connection, started: false, }; return connection; } else { return connections[messageType].connection; } } function startConnection(messageType: string) { const connectionObj = connections[messageType]; if (!connectionObj.started) { connectionObj.connection.start().catch(err => console.error('SOCKET: ', err.toString())); connectionObj.started = true; } } function stopConnection(messageType: string) { const connectionObj = connections[messageType]; if (connectionObj) { console.log('SOCKET: Stoping connection ', messageType); connectionObj.connection.stop(); connectionObj.started = false; } } function registerOnServerEvents( messageType: string, callback: (payload: Message) => void, ) { try { const connection = createConnection(messageType); connection.on('NewIncomingMessage', (payload: Message) => { callback(payload); }); connection.onclose(() => stopConnection(messageType)); startConnection(messageType); } catch (error) { console.error('SOCKET: ', error); } } export const socketService = { registerOnServerEvents, stopConnection, }; Nun könnte unsere Seite also wie im Codeausschnitt aussehen. Wir holen und extrahieren mit der Liste der Nachrichten und rendern sie. Außerdem registriert oben das Ereignis, erstellt eine Verbindung und lauscht auf das Backend. data useEffect NewMessage Wenn das Ereignis ausgelöst wird, aktualisiert die Methode des Hooks die vorhandene Liste mit einem neuen Objekt. mutate // pages/chat.ts import { useChatData } from 'hooks/useChatData'; import { socketService } from 'api/socket'; const Chat: NextPage = () => { const { data } = useChatData(); useEffect(() => { socketService.registerOnServerEvents( 'NewMessage', (payload: Message) => { mutate(() => [...data, payload], { revalidate: false }); } ); }, [data]); useEffect(() => { return () => { socketService.stopConnection('NewMessage'); }; }, []); return ( <div> {data.map(item => ( <div key={item.id}>{item.content}</div> ))} </div> ); }; Für mich sieht es gut aus, es funktioniert und wir sehen, wie neue Nachrichten im Feed erscheinen. Ich habe das einfache Beispiel mit Chat gewählt, weil es klar und leicht verständlich ist. Und natürlich wenden Sie es auf Ihre eigene Logik an. Kleiner Bonus Bei Verwendung einer der Versionen ( ) hatten wir ein Problem mit Duplikaten. Es war mit , dem Abhängigkeitsarray, verbunden. Jedes Mal, wenn die Abhängigkeit geändert wurde, Rückruf zwischengespeichert und immer wieder ausgelöst. @microsoft/signalr useEffect connection.on(event, callback); useEffect(() => { // data equals [] by default (registerOnServerEvents 1 run), // but after initial data fetching it changes (registerOnServerEvents 2 run) // each event changes data and triggers runnning of registerOnServerEvents socketService.registerOnServerEvents( 'NewMessage', // callback cached (payload: Message) => { // mutate called multiple times on each data change mutate(() => [...data, payload], { revalidate: false }); } ); }, [data]); // after getting 3 messages events, we had got 4 messages rendered lol Die schnellste und zuverlässigste Lösung, die wir gefunden haben, bestand darin, eine Kopie der Daten in der React- zu behalten und sie in für zukünftige Updates zu verwenden. ref useEffect // pages/chat.ts import { useChatData } from 'hooks/useChatData'; import { socketService } from 'api/socket'; const Chat: NextPage = () => { const { data } = useChatData(); const messagesRef = useRef<Message[]>([]); useEffect(() => { messagesRef.current = chatData; }, [chatData]); useEffect(() => { socketService.registerOnServerEvents( 'NewMessage', (payload: Message) => { const messagesCopy = messagesRef.current.slice(); mutate(() => [...messagesCopy, payload], { revalidate: false }); } ); }, [data]); useEffect(() => { return () => { socketService.stopConnection('NewMessage'); }; }, []); return ( <div> {data.map(item => ( <div key={item.id}>{item.content}</div> ))} </div> ); }; Derzeit verwenden wir eine neue Version von , die offenbar bereits notwendige Korrekturen enthält. Aber wenn jemand diese Lösung nützlich findet und diese Problemumgehung nutzt, freue ich mich trotzdem. Abschließend möchte ich sagen, dass meine Erfahrungen mit SignalR recht positiv sind, die Installation keine besonderen Abhängigkeiten oder Einstellungen erforderte und es gut funktioniert und unsere Anforderungen erfüllt. @microsoft/signalr