この記事では、Channelsモジュールを使用し、フロントサイドをReact(Next.js)、バックエンドをDjangoアプリでリアルタイム更新処理を実装する方法を解説します。
channelの実装の基本は公式ドキュメントで紹介されているチュートリアルの方法を利用し、フロントの実装内容をReact(Next.js)で行えるように編集しています。
最終的には、フロントからのイベントではなく、Djangoアプリ内部からのイベントにより、対象のフロントのページをリアルタイムで更新できるような実装とします。
◆動作検証環境
・Python:3.8.9
・Django:4.2.1
・channels : 4.0.0
・channels-redeis : 4.1.0
・daphne : 4.0.0・ redis-server : 7.0.1
・Node:16
・React:React: 18
バックエンドの実装(Django)
バックエンドは公式ドキュメントで紹介されている方法と同じ実装内容とします。
詳しいコード等は以下の記事を参考にしてください。
一部のファイルを編集します。
chat_test/chat/routing.py
1 2 3 4 5 6 7 8 9 | from django.urls import re_path from . import consumers websocket_urlpatterns = [ re_path("ws/", consumers.ChatConsumer.as_asgi()), ] |
動的なパスの設定から、固定のパスを利用します。
フロントサイドの実装(React)
npxコマンドでReactのプロジェクトを作成し以下のような構成とし、各ファイルを編集します。
対応するファイルを編集していきます。
src/index.js
1 2 3 4 5 6 7 8 9 10 11 12 | import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <React.StrictMode> <App /> </React.StrictMode> ); |
src/App.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import TopPage from "./components/TopPage"; function App() { return ( <> <TopPage/> </> ); } export default App; |
src/components/TopPage.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | import {useEffect, useRef, useState} from "react"; function TopPage() { const [sendText, setSendText] = useState('') const [text, setText] = useState('') const socketRef = useRef() useEffect(() => { const websocket = new WebSocket('ws://127.0.0.1:8000/ws/'); socketRef.current = websocket const onMessage = (e) => { const data = JSON.parse(e.data); setText((prev)=> prev + data.message + '\n') } websocket.addEventListener('message', onMessage) return () => { websocket.close() websocket.removeEventListener('message', onMessage) } }, []) return ( <> <textarea readOnly={true} cols="100" rows="20" value={text} /> <br/> <input size="100" value={sendText} onChange={(event) => setSendText(event.target.value)} /> <br/> <button type="button" onClick={() => { socketRef.current?.send( JSON.stringify({'message': sendText}) ) setSendText('') }} > Send </button> </> ); } export default TopPage; |
useRef()のhookを利用してwebsocketを扱っています。
これは再レンダリングされてもwebsocketの情報を保存しておくためです。
通常の変数はレンダリングの度に初期化されてしまいます。
フロントサイドの実装(Next.js)
Nextにて実装する場合も同じ方法となります。
Nextのプロジェクトを作成した後、pages/index.jsを以下の内容で編集します(ReactのTopPageと同じ内容です)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | import Head from 'next/head' import Image from 'next/image' import styles from '../styles/Home.module.css' import {useEffect, useRef, useState} from "react"; export default function Home() { const [sendText, setSendText] = useState('') const [text, setText] = useState('') const socketRef = useRef() useEffect(() => { const websocket = new WebSocket('ws://127.0.0.1:8000/ws/'); socketRef.current = websocket const onMessage = (e) => { const data = JSON.parse(e.data); setText((prev) => prev + data.message + '\n') } websocket.addEventListener('message', onMessage) return () => { websocket.close() websocket.removeEventListener('message', onMessage) } }, []) return ( <> <textarea readOnly={true} cols="100" rows="20" value={text} /> <br/> <input size="100" value={sendText} onChange={(event) => setSendText(event.target.value)} /> <br/> <button type="button" onClick={() => { socketRef.current?.send( JSON.stringify({'message': sendText}) ) setSendText('') }} > Send </button> </> ) } |
Djangoプロジェクト内でのwebsocket送信の実装
ピュアなJSでの実装同様、カスタムコマンドからReactの表示を変更する事ができます。
以上、Channelsを用い、ReactとDjangoアプリでリアルタイム更新処理を実装する方法を解説しました。