একটি সংখ্যালঘু, কিন্তু এখনও বিপুল সংখ্যক প্রজেক্টের, ডেটা পুনঃ-আনয়ন না করে পরিবর্তনের উপর একটি ইন্টারফেসের তাত্ক্ষণিক প্রতিক্রিয়া প্রদানের জন্য ওয়েব সকেট ইন্টিগ্রেশন প্রয়োজন।
এটি একটি অপরিহার্য জিনিস, এবং আমরা সেগুলি সম্পর্কে কথা বলতে যাচ্ছি না বা 3য়-পক্ষের লাইব্রেরির মধ্যে তুলনা করতে যাচ্ছি না যা আরও ভাল ডেভ অভিজ্ঞতার জন্য API প্রদান করে।
আমার লক্ষ্য হল কিভাবে দ্রুত @microsoft/signalr
NextJs এর সাথে সংহত করা যায় তা দেখানো। এবং উন্নয়নের সময় আমরা যে সমস্যার মুখোমুখি হয়েছি তা কীভাবে সমাধান করা যায়।
আমি আশা করি সবাই ইতিমধ্যে স্থানীয়ভাবে নেক্সটজেএস প্রকল্পটি ইনস্টল এবং স্থাপন করেছে। আমার ক্ষেত্রে, সংস্করণটি 13.2.4
। আরও কিছু গুরুত্বপূর্ণ লাইব্রেরি যোগ করা যাক: ডেটা আনার জন্য swr
(সংস্করণ 2.1.5
) এবং স্থানীয় ক্যাশে এবং @microsoft/signalr
(সংস্করণ 7.0.5
) - ওয়েব সকেটের জন্য API-এর সাথে আরও কাজ।
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
এর একটি নতুন সংস্করণ ব্যবহার করি যা ইতিমধ্যেই প্রয়োজনীয় সংশোধন করা হয়েছে বলে মনে হচ্ছে। কিন্তু যাইহোক, যদি কেউ এই সমাধানটি দরকারী খুঁজে পায় এবং এই সমাধানটি ব্যবহার করে তবে আমি খুশি হব। উপসংহারে, আমি বলতে চাই যে সিগন্যালআর-এর সাথে আমার অভিজ্ঞতা বেশ ইতিবাচক, ইনস্টলেশনের জন্য কোনও নির্দিষ্ট নির্ভরতা বা সেটিংসের প্রয়োজন হয় না এবং এটি সূক্ষ্ম কাজ করে এবং আমাদের প্রয়োজনগুলিকে কভার করে।