標題の通りsocket.ioを使って受信したデータをreactを使って作成したページにリアルタイムで更新・表示することを考えています。
このために考えた方法は以下の通りです。
- reactのcontextを使ったコンポーネント内でsocket.ioを使って初期データを受信(
initial_data_to_browser
イベント)。データ形式は連想配列(オブジェクトの配列)。 - 受信したデータはuseRefで作成したデータ更新操作用の変数(
socketData
)に保存し、このsocketData
をuseStateで作成した変数(contextData
)に保存。このcontextData
を他のコンポーネントへ渡す変数とする。 - 初期データの受信後は10秒ごとに新規データを受信(
test_emit_10s
イベント)。新規データは連想配列ではなく1つのオブジェクト。 socketData
の更新は、受信したオブジェクトを追加し、一番古いオブジェクトを削除する。- 更新後の
socketData
をsetContextData
でcontextData
に保存。
そして、作成したコードが以下です。
dataContext.tsx
(データ受信、更新用にcontextを使って作成したもの)
typescript
1import { ReactNode } from "react"; 2import {useContext, createContext, useState, useEffect, useRef} from "react"; 3import {io, Socket} from "socket.io-client"; 4 5//console.logでの日時表示用の関数 6const current_time_readable = () => { 7 let date_obj = new Date(); 8 return `${date_obj.getUTCFullYear()}-${('0' + (date_obj.getUTCMonth() + 1)).slice(-2)}-${('0' + date_obj.getUTCDate()).slice(-2)} ${('0' + date_obj.getUTCHours()).slice(-2)}:${('0' + date_obj.getUTCMinutes()).slice(-2)}:${('0' + date_obj.getUTCSeconds()).slice(-2)}`; 9}; 10 11type Props = { 12 children: ReactNode; 13} 14 15//Contextの作成。contextDataを他のコンポーネントへ渡す 16const DataContext = createContext({ 17 contextData: [] as Array<any>, 18}); 19 20//socketオブジェクトを作成して通信を開始。 21const socket: Socket = io("http://***.***.***.***"); 22 23const DataProvider = (props: Props) => { 24 //他のコンポーネントへ渡すcontextData変数をuseStateで作成 25 const [contextData, setContextData] = useState<Array<any>>([]); 26 27 //データの一時保存、更新用の変数をuseRefで作成 28 let socketData = useRef<Array<any>>([]); 29 30 useEffect(() => { 31 32 socket.on("connect_error", (e) => { 33 console.log(e); 34 }); 35 36 //初期データ受信用のsocketイベント 37 socket.on('initial_data_to_browser', (data) => { 38 //受信データをsocketDataへ保存 39 socketData.current = data; 40 41 //useRef変数の確認表示 42 console.log('initialData'); 43 console.log(socketData.current); 44 45 //contextDataへsocketDataを保存 46 setContextData(() => socketData.current); 47 48 //contextDataの確認表示(ステートの確認表示) 49 console.log('stateData'); 50 console.log(contextData); 51 }); 52 53 //新規データ受信用のsocketイベント 54 socket.on('test_emit_10s', (data) => { 55 //受信時間の表示 56 console.log(`${current_time_readable()} | test_data received`); 57 58 //古いデータを1つだけ削除 59 socketData.current.shift(); 60 61 //最新データを追加 62 socketData.current.push(data); 63 64 //データの更新結果の確認表示 65 console.log('socketData'); 66 console.log(socketData.current); 67 68 //contextDataへ更新結果を保存 69 setContextData(socketData.current); 70 71 //contextDataの確認表示(ステートの確認表示) 72 console.log('contextData'); 73 console.log(contextData); 74 }); 75 76 return () => { 77 //コンポーネントがアンマウントされた時にsocket通信を終了 78 socket.disconnect() 79 }; 80 }, []); 81 82 return ( 83 //他のコンポーネントへの出力としてcontextDataを設定 84 <DataContext.Provider value={{contextData}}> 85 {props.children} 86 </DataContext.Provider> 87 ); 88} 89 90const useData = () => useContext(DataContext); 91 92export { DataProvider, useData }
dataCard.tsx
(データ表示用のコンポーネント)
typescript
1import {VFC} from "react"; 2import {Heading} from "@chakra-ui/react"; 3 4import {useData} from "../../context/dataContext"; 5import {chartDataType} from "../../types/chartDataType"; 6 7//表示データに現在時刻を付与するための関数 8const current_time_readable = () => { 9 let date_obj = new Date(); 10 return `${date_obj.getUTCFullYear()}-${('0' + (date_obj.getUTCMonth() + 1)).slice(-2)}-${('0' + date_obj.getUTCDate()).slice(-2)} ${('0' + date_obj.getUTCHours()).slice(-2)}:${('0' + date_obj.getUTCMinutes()).slice(-2)}:${('0' + date_obj.getUTCSeconds()).slice(-2)}`; 11}; 12 13type Props = { 14 dataName: string; 15}; 16 17export const ChartCard: VFC<Props> = (props) => { 18 const {dataName} = props; 19 20 //contextDataをchartDataとして受け取り 21 const chartData = useData().contextData; 22 const propNameData: Array<chartDataType> = []; 23 24 //chartDataから特定の項目のデータを抽出して新たに配列を作成 25 if (chartData) { 26 chartData.forEach((data) => { 27 propNameData.push({ 28 value: data[dataName], 29 unixTime: data['end_time_unix'], 30 }); 31 }); 32 } 33 34 return ( 35 <> 36 { 37 //データの表示 38 propNameData ? (propNameData.map((data: any) => ( 39 <Heading as="h1" key={data.unixTime}>{`${current_time_readable()} | ${data.unixTime} | ${data.value}`}</Heading> 40 ))) : null 41 } 42 </> 43 ) 44}
上記のコードだとdataContext.tsx
では socketData
の保存・更新はうまくいっているのですが、contextData
への保存がうまくいっておらず空配列のままです(initial_data_to_browser
、test_emit_10s
のどちらのイベント発生時でも)。
dataCard.tsx
の表示は初期データはブラウザに表示されるのですが、その後の更新データは表示されません。この点に関しては、dataContext.tsx
でcontextData
は空のままなのに初期データだけがdataCard.tsx
に渡されているのが不思議です。
dataCard.tsx
はコンポーネントの上位階層で<DataProvider>
タグで囲んでいます(間にルーティングのためのコンポーネントは存在しています)。
dataCard.tsx
で初期データ・更新データをリアルタイムに表示するにはどう修正すればいいでしょうか?
よろしくお願いします。
回答1件
あなたの回答
tips
プレビュー