अल्पसंख्यक, लेकिन फिर भी बड़ी संख्या में परियोजनाओं को, डेटा को पुनः प्राप्त किए बिना परिवर्तनों पर इंटरफ़ेस की त्वरित प्रतिक्रिया प्रदान करने के लिए वेब सॉकेट एकीकरण की आवश्यकता होती है।
यह एक आवश्यक चीज़ है, और हम उनके बारे में बात नहीं करने जा रहे हैं या तीसरे पक्ष के पुस्तकालयों के बीच तुलना नहीं कर रहे हैं जो बेहतर विकास अनुभव के लिए एपीआई प्रदान करते हैं।
मेरा लक्ष्य यह दिखाना है कि @microsoft/signalr
NextJs के साथ त्वरित रूप से कैसे एकीकृत किया जाए। और विकास के दौरान हमारे सामने आने वाली समस्याओं का समाधान कैसे किया जाए।
मुझे आशा है कि सभी ने नेक्स्टजेएस प्रोजेक्ट को पहले ही स्थानीय स्तर पर स्थापित और तैनात कर लिया है। मेरे मामले में, संस्करण 13.2.4
है। आइए कुछ और महत्वपूर्ण लाइब्रेरी जोड़ें: डेटा लाने और स्थानीय कैश के साथ आगे काम करने के लिए swr
(संस्करण 2.1.5
) और @microsoft/signalr
(संस्करण 7.0.5
) - वेब सॉकेट के लिए एपीआई।
npm install --save @microsoft/signalr swr
आइए हमारे REST API से प्रारंभिक डेटा प्राप्त करने के लिए एक सरल 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, }; };
यह जांचने के लिए कि यह अपेक्षा के अनुरूप काम करता है, आइए अपने पेज घटक को अपडेट करें। शीर्ष पर हमारे हुक को आयात करें, और नीचे दिए गए स्निपेट की तरह उसमें से डेटा निकालें। यदि यह काम करता है, तो आपको प्रस्तुत डेटा दिखाई देगा। जैसा कि आप देख रहे हैं, यह काफी सरल है।
// 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> ); };
अगले चरण में हमारे भविष्य के पेज को वेब सॉकेट से कनेक्ट करना, NewMessage
ईवेंट को पकड़ना और एक नए संदेश के साथ कैश को अपडेट करना आवश्यक है। मैं एक अलग फ़ाइल में सॉकेट सेवा का निर्माण शुरू करने का प्रस्ताव करता हूं।
सिग्नलआर डॉक्स में दिए गए उदाहरणों के अनुसार, हमें आगे की घटनाओं को सुनने के लिए कनेक्शन का एक उदाहरण बनाना होगा। मैंने डुप्लिकेट को रोकने के लिए एक कनेक्शन ऑब्जेक्ट और कनेक्शन शुरू/बंद करने के लिए दो सहायक भी जोड़े।
// 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, };
तो अब, हमारा पेज कोड स्निपेट जैसा दिख सकता है। हम संदेशों की सूची के साथ data
लाते और निकालते हैं और उन्हें प्रस्तुत करते हैं। साथ ही, ऊपर दिया गया useEffect
NewMessage
इवेंट को पंजीकृत करता है, एक कनेक्शन बनाता है और बैकएंड को सुनता है।
जब ईवेंट ट्रिगर होता है, तो हुक से 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> ); };
मुझे अच्छा लग रहा है, यह काम करता है, और हम देखते हैं कि फ़ीड में नए संदेश कैसे दिखाई देते हैं। मैंने चैट के साथ मूल उदाहरण चुना क्योंकि यह स्पष्ट और समझने में आसान है। और, निःसंदेह, आप इसे अपने तर्क पर लागू करते हैं।
एक संस्करण ( @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
सबसे तेज़ और सबसे विश्वसनीय समाधान जो हमें मिला, वह था रिएक्ट 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> ); };
वर्तमान में, हम @microsoft/signalr
के एक नए संस्करण का उपयोग करते हैं, ऐसा लगता है कि इसमें पहले से ही आवश्यक सुधार हैं। लेकिन फिर भी, अगर कोई इस समाधान को उपयोगी पाता है और इस समाधान का उपयोग करता है, तो मुझे खुशी होगी। अंत में, मैं कहना चाहता हूं कि सिग्नलआर के साथ मेरा अनुभव काफी सकारात्मक है, इंस्टॉलेशन के लिए किसी विशिष्ट निर्भरता या सेटिंग्स की आवश्यकता नहीं है, और यह ठीक से काम करता है और हमारी जरूरतों को पूरा करता है।