Một số ít, nhưng vẫn là một số lượng lớn các dự án, yêu cầu tích hợp ổ cắm web để cung cấp phản ứng tức thời của giao diện đối với các thay đổi mà không cần tìm nạp lại dữ liệu. Đó là một điều thiết yếu và chúng tôi sẽ không nói về chúng hoặc so sánh giữa các thư viện của bên thứ 3 cung cấp API để có trải nghiệm tốt hơn cho nhà phát triển. Mục tiêu của tôi là trình bày cách tích hợp nhanh chóng với NextJs. Và cách giải quyết những vấn đề chúng tôi gặp phải trong quá trình phát triển. @microsoft/signalr Tôi hy vọng mọi người đã cài đặt và triển khai dự án NextJS tại địa phương. Trong trường hợp của tôi, phiên bản là . Hãy thêm một số thư viện quan trọng hơn: (phiên bản ) để tìm nạp dữ liệu và làm việc thêm với bộ đệm cục bộ và (phiên bản ) - API cho ổ cắm web. 13.2.4 swr 2.1.5 @microsoft/signalr 7.0.5 npm install --save @microsoft/signalr swr Hãy bắt đầu bằng việc tạo một hàm đơn giản và một hook mới có tên để lấy dữ liệu ban đầu từ API REST của chúng tôi. Nó trả về danh sách các tin nhắn dành cho cuộc trò chuyện, các trường phát hiện lỗi và trạng thái tải cũng như phương thức cho phép thay đổi dữ liệu được lưu trong bộ nhớ đệm. 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, }; }; Để kiểm tra xem nó có hoạt động như mong đợi hay không, hãy cập nhật thành phần trang của chúng tôi. Nhập hook của chúng tôi ở trên cùng và trích xuất dữ liệu từ nó như trong đoạn mã bên dưới. Nếu nó hoạt động, bạn sẽ thấy dữ liệu được hiển thị. Như bạn thấy, nó khá đơn giản. // 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> ); }; Bước tiếp theo yêu cầu kết nối trang tương lai của chúng tôi với các ổ cắm web, nắm bắt các sự kiện và cập nhật bộ đệm bằng một tin nhắn mới. Tôi đề xuất bắt đầu bằng việc xây dựng dịch vụ socket trong một tệp riêng biệt. NewMessage Theo các ví dụ trong tài liệu SignalR, chúng tôi phải tạo một phiên bản kết nối để nghe thêm các sự kiện. Tôi cũng đã thêm một đối tượng kết nối để ngăn chặn sự trùng lặp và hai đối tượng trợ giúp để bắt đầu/dừng kết nối. // 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, }; Bây giờ, trang của chúng ta có thể trông giống như đoạn mã. Chúng tôi tìm nạp và trích xuất cùng với danh sách tin nhắn và hiển thị chúng. Ngoài ra, ở trên sẽ đăng ký sự kiện , tạo kết nối và lắng nghe phần phụ trợ. data useEffect NewMessage Khi sự kiện kích hoạt, phương thức từ hook sẽ cập nhật danh sách hiện có bằng một đối tượng mới. 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> ); }; Với tôi thì có vẻ ổn, nó hoạt động và chúng tôi thấy các tin nhắn mới xuất hiện như thế nào trong nguồn cấp dữ liệu. Tôi chọn ví dụ cơ bản về trò chuyện vì nó rõ ràng và dễ hiểu. Và tất nhiên, bạn áp dụng nó theo logic của riêng mình. Tiền thưởng nhỏ Khi sử dụng một trong các phiên bản ( ), chúng tôi gặp phải sự cố trùng lặp. Nó được kết nối với , mảng phụ thuộc. Mỗi lần phần phụ thuộc được thay đổi, gọi lại được lưu trong bộ nhớ cache và kích hoạt nó nhiều lần. @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 Giải pháp nhanh nhất và đáng tin cậy nhất mà chúng tôi tìm thấy là giữ một bản sao dữ liệu bên trong React và sử dụng nó bên trong cho các bản cập nhật trong tương lai. 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> ); }; Hiện tại, chúng tôi sử dụng phiên bản mới của và có vẻ như phiên bản này đã có các bản sửa lỗi cần thiết. Nhưng dù sao đi nữa, nếu ai đó thấy giải pháp này hữu ích và sử dụng cách giải quyết này thì tôi sẽ rất vui. Để kết luận, tôi muốn nói rằng trải nghiệm của tôi với SignalR khá tích cực, việc cài đặt không yêu cầu bất kỳ sự phụ thuộc hoặc cài đặt cụ thể nào và nó hoạt động tốt và đáp ứng nhu cầu của chúng tôi. @microsoft/signalr