Azınlık ama yine de çok sayıda proje, verileri yeniden getirmeden bir arayüzün değişikliklere anında tepki vermesini sağlamak için web soketleri entegrasyonu gerektirir. Bu çok önemli bir şey ve biz onlar hakkında konuşmayacağız veya daha iyi bir geliştirici deneyimi için API sağlayan 3. taraf kütüphaneler arasında bir karşılaştırma yapmayacağız. Amacım NextJs ile hızlı bir şekilde nasıl entegre edileceğini göstermek. Ve geliştirme sırasında karşılaştığımız sorunları nasıl çözeceğimizi. @microsoft/signalr Umarım herkes NextJS projesini yerel olarak kurmuş ve konuşlandırmıştır. Benim durumumda sürüm . Biraz daha önemli kütüphaneler ekleyelim: veri almak ve yerel önbellekle daha fazla çalışmak için (sürüm ) ve web yuvaları için (sürüm ) - API. 13.2.4 swr 2.1.5 @microsoft/signalr 7.0.5 npm install --save @microsoft/signalr swr REST API'mizden ilk verileri almak için basit bir işlevi ve adında yeni bir kanca oluşturmaya başlayalım. Sohbete ilişkin mesajların bir listesini, hataları algılayan alanları ve yükleme durumunu ve önbelleğe alınan verileri değiştirmeye izin veren yöntemini döndürür. 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, }; }; Beklendiği gibi çalıştığını test etmek için sayfa bileşenimizi güncelleyelim. En üstteki kancamızı içe aktarın ve aşağıdaki kod parçasındaki gibi verileri çıkarın. Çalışırsa, işlenmiş verileri göreceksiniz. Gördüğünüz gibi oldukça basit. // 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> ); }; Bir sonraki adım, gelecekteki sayfamızı web soketlerine bağlamayı, olaylarını yakalamayı ve önbelleği yeni bir mesajla güncellemeyi gerektirir. Soket hizmetini ayrı bir dosyada oluşturmaya başlamayı öneriyorum. NewMessage SignalR belgelerindeki örneklere göre, olayları daha fazla dinlemek için bir bağlantı örneği oluşturmamız gerekiyor. Ayrıca yinelemeleri önlemek için bir bağlantı nesnesi ve bağlantıları başlatmak/durdurmak için iki yardımcı ekledim. // 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, }; Artık sayfamız kod pasajındaki gibi görünebilir. Mesaj listesiyle alıp çıkarıyoruz ve bunları oluşturuyoruz. Ayrıca yukarıdaki , olayını kaydeder, bir bağlantı oluşturur ve arka ucu dinler. data useEffect NewMessage Olay tetiklendiğinde, kancadaki yöntemi mevcut listeyi yeni bir nesneyle günceller. 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> ); }; Bana güzel görünüyor, işe yarıyor ve yeni mesajların akışta nasıl göründüğünü görüyoruz. Temel örneği sohbetle seçtim çünkü açık ve anlaşılması kolay. Ve elbette bunu kendi mantığınıza göre uygularsınız. Küçük Bonus Sürümlerden birini ( ) kullanırken çoğaltmalarla ilgili bir sorunla karşılaştık. Bağımlılık dizisi olan bağlandı. Bağımlılık her değiştirildiğinde, geri aramayı önbelleğe aldı ve tekrar tekrar tetikledi. @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 Bulduğumuz en hızlı ve en güvenilir çözüm, verilerin bir kopyasını React içinde tutmak ve bunu gelecekteki güncellemeler için içinde kullanmaktı. 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> ); }; Şu anda gerekli düzeltmeleri içeren yeni bir sürümünü kullanıyoruz. Ama yine de birisi bu çözümü yararlı bulursa ve bu geçici çözümü kullanırsa mutlu olacağım. Sonuç olarak, SignalR ile olan deneyimimin oldukça olumlu olduğunu, kurulumun herhangi bir özel bağımlılık veya ayar gerektirmediğini, gayet iyi çalıştığını ve ihtiyaçlarımızı karşıladığını söylemek istiyorum. @microsoft/signalr