[解決済み] Next.jsでWebSocketを利用する
質問
Next.jsのページを使用してWebSocketサーバーに接続するための最良の方法は何でしょうか?私は、ユーザーが1つの接続でページ間を移動でき、ページを閉じるときにWebSockets接続も閉じられるようにしたいです。私はReactのContext APIを使用しようとしました。
const WSContext = createContext(null);
const Wrapper = ({ children }) => {
const instance = WebSocket("ws://localhost:3000/ws");
return <WSContext.Provider value={instance}>{children}</WSContext.Provider>;
};
export const useWS = () => useContext(WSContext);
export default Wrapper;
これは素晴らしい働きですが、接続を作成するときにはそうではありません。基本的な
new WebSocket
の構文が使えないので、サードパーティのライブラリ、例えば
react-use-websocket
というのが嫌なんです。さらに気になるのは、接続を閉じることができないことです。Contextはページが閉じられたことを知らないだけで、ライブラリは接続を閉じるためのフックを提供していない。
Next.jsでWebSocketの接続を処理する場合、どのような方法があるのか教えてほしいです。
どのように解決するのですか?
Next.jsでwsを動作させるためには、複数の事柄を行う必要があります。
まず、wsのコードをどこで実行させたいかを認識することが重要です。Next.js上のReactのコードは、2つの環境で動作します。サーバー上(ページを構築するときやssrを使用するとき)とクライアント上です。
ページ構築時にws接続を行うことはほとんど意味がないので、ここではクライアントサイドのwsのみを取り上げます。
グローバルなWebsocketクラスは、ブラウザのみの機能であり、サーバには存在しない。そのため、ブラウザでコードが実行されるまで、インスタンス化を防止する必要があります。そのための簡単な方法の1つは、次のようなものです。
export const isBrowser = typeof window !== "undefined";
export const wsInstance = isBrowser ? new Websocket(...) : null;
また、インスタンスを保持するためにreact contextを使用する必要はなく、グローバルスコープに保持してインスタンスをインポートすることは、接続を遅延して開きたい場合を除き、完全に可能である。
それでもリアクトコンテキストを使う(あるいはリアクトツリーのどこかでwsクライアントを初期化する)場合は、次のことが重要です。 メモする のインスタンスは、リアクトノードの更新のたびに生成されないようにします。
const wsInstance = useMemo(() => isBrowser ? new Websocket(...) : null, []);
または
const [wsInstance] = useState(() => isBrowser ? new Websocket(...) : null);
reactで作成されたイベントハンドラ登録はすべて
useEffect
これはメモリリークを防ぐためです。また、依存関係の配列も指定する必要があります。コンポーネントがアンマウントされた場合は
useEffect
フックは、イベントリスナーも削除します。
もし、ws を再定義して、現在の接続を破棄したい場合は、以下のような方法が可能です。
const [wsInstance, setWsInstance] = useState(null);
// Call when updating the ws connection
const updateWs = useCallback((url) => {
if(!browser) return setWsInstance(null);
// Close the old connection
if(wsInstance?.readyState !== 3)
wsInstance.close(...);
// Create a new connection
const newWs = new WebSocket(url);
setWsInstance(newWs);
}, [wsInstance])
// (Optional) Open a connection on mount
useEffect(() => {
if(isBrowser) {
const ws = new WebSocket(...);
setWsInstance(ws);
}
return () => {
// Cleanup on unmount if ws wasn't closed already
if(ws?.readyState !== 3)
ws.close(...)
}
}, [])
関連
-
[解決済み】コンポーネントの定義に表示名がない react/display-name
-
[解決済み] React の open mailto E-Mail クライアントの onClick で textarea から本文を取得する。
-
[解決済み] Uncaught (in promise) Error: リクエストに失敗、ステータスコード404
-
[解決済み] 矢印本体を囲む予期せぬブロックステートメント
-
[解決済み] react-router-domを使用する際に「Function components cannot be given refs」を回避する方法は?
-
[解決済み] Reactでグローバル変数を宣言する方法とは?
-
[解決済み] Reactルータを使ったプログラムによるナビゲーション
-
[解決済み] Long-Polling、Websocket、Server-Sent Events (SSE)、Cometとは何ですか?
-
[解決済み] WebSocketとサーバー送信型イベントの比較/EventSource
-
[解決済み] socket.ioとwebsocketの違いについて
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] エラー: 未定義のプロパティ 'map' を読み取ることができません。
-
[解決済み】ngrokがReact devサーバーに接続しようとすると、無効なホストヘッダが表示される。
-
[解決済み】Reactでclsxを使用する方法
-
[解決済み] reactでonloadを使うには?
-
[解決済み] Next.jsでWebSocketを利用する
-
[解決済み] 'Proptypes'が定義されていない
-
[解決済み] nextjsで異なる.envファイルを使用するには?
-
[解決済み] 矢印本体を囲む予期せぬブロックステートメント
-
[解決済み] 拡張子.tsと.tsxの違いは何ですか?どちらもreactのタイプスクリプトファイルの拡張子として使用されます。では、どこで使うべきなのでしょうか?
-
[解決済み] reactのuseStateフックでコールバックを使う方法 [重複]。