Reactでデータをストリーミング形式で取得しリアルタイム表示する方法

ストリーミングデータの表示は、リアルタイムアプリケーションや動的なデータ処理を必要とする場面で不可欠な技術です。株価の変動、スポーツスコアの更新、チャットメッセージのやり取りなど、リアルタイム性が求められるシナリオでは、効率的かつ安定したデータ取得と更新が鍵となります。

Reactは、その状態管理の効率性やコンポーネントベースの設計により、こうしたリアルタイムアプリケーションに適したフレームワークです。本記事では、Reactを使用してストリーミングデータを取得し、画面にリアルタイム表示する方法を詳細に解説します。基本的な概念から始め、実用的なサンプルコードや応用例、さらには課題を通じて、ストリーミングデータの処理を実践的に学ぶ機会を提供します。

リアルタイムアプリケーションの開発を考えている方や、Reactを使ったデータ処理に興味のある方は、ぜひ最後までお読みください。

目次
  1. Reactでのデータストリーミングの基本概念
    1. ストリーミングデータとは
    2. Reactの特徴とストリーミングデータ
    3. データ取得の一般的な手法
  2. WebSocketの導入と設定方法
    1. WebSocketとは
    2. WebSocketのセットアップ手順
    3. 必要な依存パッケージ
    4. WebSocket利用時の注意点
  3. サンプルコードで学ぶストリーミング処理
    1. Reactアプリケーションでのストリーミング実装
    2. コードのポイント解説
    3. 実行手順
    4. 動作確認
  4. 状態管理と効率的な更新の実現
    1. リアルタイム更新におけるReactの状態管理
    2. ストリーミングデータと状態の構造
    3. 再レンダリングの最適化
    4. 大規模データの効率的なレンダリング
    5. パフォーマンスを意識したUI更新
  5. エラーハンドリングと再接続の実装
    1. リアルタイムアプリケーションにおけるエラーハンドリングの重要性
    2. WebSocketにおけるエラーハンドリング
    3. 再接続の実装
    4. 接続の安定性を向上させるヒント
  6. 高パフォーマンスなUIの設計
    1. リアルタイムアプリケーションにおけるUI設計の課題
    2. レンダリング効率の向上
    3. ユーザーの視認性を高めるUI設計
    4. 負荷を軽減するための工夫
  7. 応用例: 株価やチャットアプリの構築
    1. リアルタイムデータの応用例
    2. 応用例1: 株価表示システム
    3. 応用例2: リアルタイムチャットアプリ
    4. まとめ
  8. 演習: ストリーミングアプリの作成
    1. 演習の目的
    2. 演習課題
    3. 演習の要件
    4. コード例
    5. 演習手順
    6. 学習成果
  9. まとめ

Reactでのデータストリーミングの基本概念

ストリーミングデータとは


ストリーミングデータは、リアルタイムで生成される情報の連続的な流れを指します。例えば、センサーから送られるデータや、ソーシャルメディアでのライブ更新、株価の変動情報などが該当します。このデータは常に変化し、リアルタイムで処理と表示が求められます。

Reactの特徴とストリーミングデータ


Reactは、状態管理と効率的なUI再レンダリングが特徴のフロントエンドライブラリです。ストリーミングデータと組み合わせることで、動的に更新される画面をスムーズに構築できます。
特に、ReactのVirtual DOMは頻繁な更新に最適で、リアルタイム性を損なうことなく効率的にUIを再描画できます。

データ取得の一般的な手法


ストリーミングデータをReactアプリケーションに取り込む一般的な手法は以下の通りです。

  • WebSocket: 双方向通信が可能で、リアルタイム更新に適したプロトコルです。
  • Server-Sent Events (SSE): サーバーがクライアントにデータをプッシュする方式。
  • Polling: 短い間隔でサーバーにリクエストを送り、データを取得する方式。

これらの中で、WebSocketはリアルタイム性が最も高く、ストリーミングデータに頻繁に使用されます。本記事では、このWebSocketを基盤としたストリーミングデータの取得とReactでの活用方法を中心に解説していきます。

WebSocketの導入と設定方法

WebSocketとは


WebSocketは、クライアントとサーバー間での双方向通信を可能にするプロトコルです。HTTPとは異なり、接続が一度確立されると、クライアントとサーバーは継続的にデータを送受信できます。この特徴により、リアルタイムアプリケーションに非常に適しています。

WebSocketのセットアップ手順


ReactアプリケーションでWebSocketを使用するには、以下の手順を踏みます。

1. WebSocketサーバーの準備


ストリーミングデータを提供するサーバーが必要です。Node.jsを使用して簡単なWebSocketサーバーを構築できます。以下はその例です。

const WebSocket = require('ws');

const server = new WebSocket.Server({ port: 8080 });

server.on('connection', (socket) => {
    console.log('Client connected');
    // サーバーからデータを送信
    setInterval(() => {
        const data = { message: 'Hello from server', timestamp: Date.now() };
        socket.send(JSON.stringify(data));
    }, 1000);

    socket.on('close', () => console.log('Client disconnected'));
});

2. ReactでWebSocketを接続する


クライアントサイドでWebSocketを使用するには、WebSocketオブジェクトを利用します。

import React, { useEffect, useState } from 'react';

const WebSocketExample = () => {
    const [messages, setMessages] = useState([]);

    useEffect(() => {
        const socket = new WebSocket('ws://localhost:8080');

        socket.onopen = () => {
            console.log('WebSocket connected');
        };

        socket.onmessage = (event) => {
            const data = JSON.parse(event.data);
            setMessages((prev) => [...prev, data]);
        };

        socket.onclose = () => {
            console.log('WebSocket disconnected');
        };

        return () => socket.close();
    }, []);

    return (
        <div>
            <h2>Real-time Messages</h2>
            <ul>
                {messages.map((msg, index) => (
                    <li key={index}>
                        {msg.message} - {new Date(msg.timestamp).toLocaleTimeString()}
                    </li>
                ))}
            </ul>
        </div>
    );
};

export default WebSocketExample;

必要な依存パッケージ

  • WebSocketサーバーを作成する場合、wsパッケージをインストールします。
  npm install ws
  • React側では特別なパッケージは必要ありませんが、useEffectuseStateを活用します。

WebSocket利用時の注意点

  1. セキュリティ: HTTPSを使用している場合、wss://プロトコルでWebSocketを使用する必要があります。
  2. エラーハンドリング: 接続エラーやサーバー切断に備えたエラー処理を必ず実装します。
  3. スケーラビリティ: 高負荷環境でWebSocketを使用する場合、スケーリングを考慮した設計が必要です。

WebSocketを導入することで、Reactアプリケーションにリアルタイム性を容易に加えることが可能です。次項では、この仕組みを利用したサンプルコードをさらに詳しく解説します。

サンプルコードで学ぶストリーミング処理

Reactアプリケーションでのストリーミング実装


以下は、WebSocketを使用してリアルタイムでストリーミングデータを取得し、Reactで表示する完全なサンプルコードです。このコードでは、簡単なチャットメッセージの受信をシミュレーションします。

1. プロジェクトのセットアップ


以下のコマンドでReactプロジェクトをセットアップします。

npx create-react-app streaming-example
cd streaming-example

2. WebSocket接続とストリーミング


次に、以下のコードをApp.jsに書き込みます。

import React, { useEffect, useState } from 'react';

const App = () => {
    const [messages, setMessages] = useState([]);
    const [isConnected, setIsConnected] = useState(false);

    useEffect(() => {
        const socket = new WebSocket('ws://localhost:8080');

        socket.onopen = () => {
            console.log('WebSocket connected');
            setIsConnected(true);
        };

        socket.onmessage = (event) => {
            const data = JSON.parse(event.data);
            setMessages((prev) => [...prev, data]);
        };

        socket.onerror = (error) => {
            console.error('WebSocket error:', error);
        };

        socket.onclose = () => {
            console.log('WebSocket disconnected');
            setIsConnected(false);
        };

        return () => {
            socket.close();
        };
    }, []);

    return (
        <div>
            <h1>React Streaming Example</h1>
            <h2>Connection Status: {isConnected ? 'Connected' : 'Disconnected'}</h2>
            <div>
                <h3>Messages:</h3>
                <ul>
                    {messages.map((msg, index) => (
                        <li key={index}>
                            {msg.message} - {new Date(msg.timestamp).toLocaleTimeString()}
                        </li>
                    ))}
                </ul>
            </div>
        </div>
    );
};

export default App;

コードのポイント解説

WebSocket接続の初期化


useEffect内でWebSocketを初期化し、コンポーネントがマウントされたときに接続を確立します。

const socket = new WebSocket('ws://localhost:8080');

データの受信


onmessageイベントで受信したデータをJSON形式に解析し、Reactの状態として保存します。

socket.onmessage = (event) => {
    const data = JSON.parse(event.data);
    setMessages((prev) => [...prev, data]);
};

接続ステータスの表示


isConnected状態を使用して、WebSocketの接続状態をリアルタイムでUIに反映します。

<h2>Connection Status: {isConnected ? 'Connected' : 'Disconnected'}</h2>

実行手順

  1. 先述したNode.jsのWebSocketサーバーを起動します。
  2. Reactアプリケーションを起動します。
   npm start
  1. ブラウザでhttp://localhost:3000にアクセスします。

動作確認

  • WebSocketサーバーがメッセージを送信するたびに、Reactアプリケーションのメッセージリストがリアルタイムで更新されます。
  • 接続ステータスも動的に表示され、サーバー切断時には”Disconnected”が表示されます。

このサンプルコードにより、Reactでのストリーミング処理の基礎を学ぶことができます。次項では、状態管理と効率的な更新方法についてさらに掘り下げて解説します。

状態管理と効率的な更新の実現

リアルタイム更新におけるReactの状態管理


Reactは状態管理を通じて、動的なUIのレンダリングを効率化します。ストリーミングデータの処理では、頻繁に更新されるデータを効率的に扱うことが重要です。ここでは、ストリーミングデータの管理とReact状態の適切な設計について解説します。

ストリーミングデータと状態の構造


ストリーミングデータを状態に格納する際は、以下の点を考慮します。

  1. パフォーマンス最適化: 不要な再レンダリングを防ぐため、状態の粒度を適切に設計します。
  2. 無限リストの処理: データが増え続ける場合に、古いデータの削除やメモリ効率を考慮します。

以下は、useStateフックを用いた効率的な状態管理の例です。

const [messages, setMessages] = useState([]);
const MAX_MESSAGES = 100; // 最大保持メッセージ数

const addMessage = (newMessage) => {
    setMessages((prev) => {
        const updatedMessages = [...prev, newMessage];
        if (updatedMessages.length > MAX_MESSAGES) {
            updatedMessages.shift(); // 古いメッセージを削除
        }
        return updatedMessages;
    });
};

再レンダリングの最適化

1. Reactの`useMemo`と`useCallback`の活用


データが頻繁に更新される場合、パフォーマンスを向上させるために、再計算や再生成を最小限に抑えます。

import React, { useMemo, useCallback } from 'react';

const MemoizedComponent = ({ messages }) => {
    const renderedMessages = useMemo(() => {
        return messages.map((msg, index) => (
            <li key={index}>
                {msg.message} - {new Date(msg.timestamp).toLocaleTimeString()}
            </li>
        ));
    }, [messages]);

    return <ul>{renderedMessages}</ul>;
};

2. バッチ更新でパフォーマンス向上


React 18以降では、複数の状態更新をバッチ処理としてまとめて行います。

import { flushSync } from 'react-dom';

const handleMultipleUpdates = (newMessages) => {
    flushSync(() => {
        setMessages((prev) => [...prev, ...newMessages]);
    });
};

大規模データの効率的なレンダリング


大量のデータをリアルタイムでレンダリングする際には、仮想化(virtualization)を活用します。
react-windowreact-virtualizedライブラリを使用することで、画面外の非表示データのレンダリングを抑え、パフォーマンスを向上できます。

npm install react-window

以下は、react-windowを使用した例です。

import { FixedSizeList as List } from 'react-window';

const VirtualizedList = ({ messages }) => (
    <List
        height={400}
        itemCount={messages.length}
        itemSize={35}
        width={300}
    >
        {({ index, style }) => (
            <div style={style}>
                {messages[index].message} - {new Date(messages[index].timestamp).toLocaleTimeString()}
            </div>
        )}
    </List>
);

パフォーマンスを意識したUI更新


リアルタイムデータの表示では、以下の戦略が推奨されます。

  1. 状態の部分的更新: 必要な部分だけ状態を変更し、再レンダリングを抑制します。
  2. 遅延更新: 高頻度の更新をまとめて処理するため、setTimeoutdebounceを利用します。
  3. 非同期処理: データ処理が重い場合は、非同期関数を活用してメインスレッドの負荷を軽減します。

これらのテクニックにより、リアルタイム更新でもスムーズなUI体験を提供できるReactアプリケーションを構築できます。次項では、エラー処理と再接続の方法を解説します。

エラーハンドリングと再接続の実装

リアルタイムアプリケーションにおけるエラーハンドリングの重要性


リアルタイムストリーミングでは、ネットワークの不安定さやサーバー障害が原因でエラーが発生する可能性があります。これらの問題に適切に対応することで、アプリケーションの信頼性を向上させることができます。

本節では、WebSocketを使用したアプリケーションでのエラーハンドリングと、接続が切断された際の自動再接続の実装方法を説明します。

WebSocketにおけるエラーハンドリング


WebSocketでは、以下のようなイベントハンドラを使用してエラーをキャッチします。

  • onerror: 接続中のエラーをキャッチします。
  • onclose: 接続が閉じられた場合に呼び出されます。

以下はエラーハンドリングの例です。

useEffect(() => {
    const socket = new WebSocket('ws://localhost:8080');

    socket.onopen = () => {
        console.log('WebSocket connected');
    };

    socket.onerror = (error) => {
        console.error('WebSocket encountered an error:', error);
    };

    socket.onclose = (event) => {
        if (!event.wasClean) {
            console.error('WebSocket closed unexpectedly:', event);
        } else {
            console.log('WebSocket closed cleanly');
        }
    };

    return () => socket.close();
}, []);

再接続の実装


ネットワーク切断やサーバーダウン時に、自動的に再接続を試みることで、アプリケーションの中断を最小限に抑えます。

再接続ロジックの例

import React, { useEffect, useState } from 'react';

const WebSocketWithReconnect = () => {
    const [socket, setSocket] = useState(null);
    const [isConnected, setIsConnected] = useState(false);
    const [messages, setMessages] = useState([]);
    const reconnectInterval = 5000; // 再接続間隔(ミリ秒)

    const connectWebSocket = () => {
        const newSocket = new WebSocket('ws://localhost:8080');

        newSocket.onopen = () => {
            console.log('WebSocket connected');
            setIsConnected(true);
        };

        newSocket.onmessage = (event) => {
            const data = JSON.parse(event.data);
            setMessages((prev) => [...prev, data]);
        };

        newSocket.onerror = (error) => {
            console.error('WebSocket error:', error);
        };

        newSocket.onclose = (event) => {
            console.log('WebSocket disconnected. Attempting to reconnect...');
            setIsConnected(false);
            setTimeout(() => connectWebSocket(), reconnectInterval);
        };

        setSocket(newSocket);
    };

    useEffect(() => {
        connectWebSocket();
        return () => {
            if (socket) socket.close();
        };
    }, []);

    return (
        <div>
            <h1>WebSocket with Reconnection</h1>
            <h2>Status: {isConnected ? 'Connected' : 'Disconnected'}</h2>
            <ul>
                {messages.map((msg, index) => (
                    <li key={index}>
                        {msg.message} - {new Date(msg.timestamp).toLocaleTimeString()}
                    </li>
                ))}
            </ul>
        </div>
    );
};

export default WebSocketWithReconnect;

ポイント解説

  1. setTimeoutを使用した再接続: 接続が切断された際に、一定時間後に再接続を試みます。
  2. 状態管理: isConnectedで接続状況を管理し、UIに反映させます。
  3. useEffectのクリーンアップ: コンポーネントのアンマウント時にWebSocket接続を確実に閉じます。

接続の安定性を向上させるヒント

  1. バックオフ戦略: 再接続試行回数が増えるたびに、待機時間を指数関数的に増加させる。
  2. サーバー側のタイムアウト処理: クライアントが応答しない場合に接続を明示的に閉じる。
  3. Ping/Pongメカニズム: クライアントとサーバー間で定期的に通信を行い、接続が維持されていることを確認する。

エラーハンドリングと再接続の仕組みを組み込むことで、安定したリアルタイムアプリケーションを提供できます。次項では、リアルタイムUI設計に必要な高パフォーマンスなアプローチについて解説します。

高パフォーマンスなUIの設計

リアルタイムアプリケーションにおけるUI設計の課題


リアルタイムでデータを表示する際には、頻繁なデータ更新に伴う再レンダリングや、画面の視認性の確保が課題となります。高パフォーマンスなUI設計では、以下の点を考慮する必要があります。

  1. レンダリング効率の最適化: 更新が必要な部分だけを効率的に描画する。
  2. 視認性の向上: リアルタイムで変化する情報が、ユーザーにとって分かりやすく表示される。
  3. 負荷軽減: 無駄なレンダリングや非効率的な処理を避ける。

以下では、これらの課題を解決するための具体的な設計手法を紹介します。

レンダリング効率の向上

1. コンポーネント分割


コンポーネントを小さく分割し、更新が必要な箇所のみ再レンダリングする設計を採用します。React.memoを利用して、プロパティに変化がない場合に再レンダリングを防ぐことができます。

import React from 'react';

const MessageItem = React.memo(({ message, timestamp }) => {
    return (
        <li>
            {message} - {new Date(timestamp).toLocaleTimeString()}
        </li>
    );
});

export default MessageItem;

2. 仮想化の活用


大量のデータを表示する場合、仮想化ライブラリ(例: react-window)を使用して、画面内の要素だけをレンダリングします。

import { FixedSizeList as List } from 'react-window';

const VirtualizedList = ({ messages }) => (
    <List
        height={400}
        itemCount={messages.length}
        itemSize={35}
        width={300}
    >
        {({ index, style }) => (
            <div style={style}>
                {messages[index].message} - {new Date(messages[index].timestamp).toLocaleTimeString()}
            </div>
        )}
    </List>
);

3. バッチ更新


状態の更新が頻繁に行われる場合、Reactのバッチ更新機能を利用します。これにより、複数の状態変更を1回の再レンダリングでまとめて処理できます。

import { flushSync } from 'react-dom';

const updateMessages = (newMessages) => {
    flushSync(() => {
        setMessages((prev) => [...prev, ...newMessages]);
    });
};

ユーザーの視認性を高めるUI設計

1. 新しいデータの強調表示


リアルタイム更新では、新しく追加されたデータを一時的にハイライトすることで、ユーザーに変化を認識させます。

const [highlighted, setHighlighted] = useState(null);

useEffect(() => {
    if (messages.length > 0) {
        setHighlighted(messages[messages.length - 1].id);
        setTimeout(() => setHighlighted(null), 2000); // ハイライト解除
    }
}, [messages]);

const renderMessage = (msg) => (
    <li key={msg.id} style={{ background: msg.id === highlighted ? '#ffeb3b' : 'transparent' }}>
        {msg.message} - {new Date(msg.timestamp).toLocaleTimeString()}
    </li>
);

2. スクロール位置の管理


新しいデータが追加された際、スクロール位置を適切に管理します。以下は、リストが末尾にスクロールする例です。

const messagesEndRef = useRef(null);

useEffect(() => {
    messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
}, [messages]);

return (
    <div>
        <ul>
            {messages.map((msg) => renderMessage(msg))}
        </ul>
        <div ref={messagesEndRef} />
    </div>
);

負荷を軽減するための工夫

1. データ更新の制限


高頻度の更新を軽減するため、サーバーから受信したデータを間引いて処理します。

const throttledUpdate = _.throttle((newData) => {
    setMessages((prev) => [...prev, newData]);
}, 500); // 更新間隔を500msに制限

2. 優先度ベースのレンダリング


非同期で優先度を設定し、必要に応じてレンダリングを後回しにする。React Concurrent Modeを利用することでこれが可能です。


これらの設計技術を活用することで、ユーザーにとってスムーズで視認性の高いリアルタイムUIを構築できます。次項では、応用例として株価やチャットアプリを構築する方法を解説します。

応用例: 株価やチャットアプリの構築

リアルタイムデータの応用例


リアルタイムデータを活用したアプリケーションとして、特に代表的な例が株価表示システムチャットアプリです。これらの応用例を通じて、実際のストリーミングデータをReactでどのように扱うかを解説します。


応用例1: 株価表示システム

特徴


株価表示アプリでは、リアルタイムのデータ更新と、急激な変化を視覚的にわかりやすく表示する機能が求められます。価格の上昇や下降を色やアイコンで示す設計を行います。

実装例

import React, { useEffect, useState } from 'react';

const StockApp = () => {
    const [stocks, setStocks] = useState([]);

    useEffect(() => {
        const socket = new WebSocket('wss://example.com/stocks');

        socket.onmessage = (event) => {
            const updatedStocks = JSON.parse(event.data);
            setStocks(updatedStocks);
        };

        return () => socket.close();
    }, []);

    const getChangeClass = (change) => {
        return change > 0 ? 'stock-up' : 'stock-down';
    };

    return (
        <div>
            <h1>Live Stock Prices</h1>
            <table>
                <thead>
                    <tr>
                        <th>Symbol</th>
                        <th>Price</th>
                        <th>Change</th>
                    </tr>
                </thead>
                <tbody>
                    {stocks.map((stock) => (
                        <tr key={stock.symbol}>
                            <td>{stock.symbol}</td>
                            <td>{stock.price.toFixed(2)}</td>
                            <td className={getChangeClass(stock.change)}>
                                {stock.change > 0 ? '▲' : '▼'} {stock.change.toFixed(2)}
                            </td>
                        </tr>
                    ))}
                </tbody>
            </table>
        </div>
    );
};

export default StockApp;

動作説明

  • リアルタイム更新: WebSocketで株価情報を定期的に受信し、状態を更新します。
  • 視覚的フィードバック: getChangeClass関数で価格の増減に応じてクラスを変更し、CSSで色分けします。

追加機能の提案

  • 株価のチャート描画(ライブラリ例: chart.js
  • アラート通知(価格が指定値に到達した場合)

応用例2: リアルタイムチャットアプリ

特徴


チャットアプリでは、メッセージの即時反映と、複数ユーザー間のリアルタイム通信が重要です。また、メッセージの送信者やタイムスタンプの表示も必要です。

実装例

import React, { useState, useEffect } from 'react';

const ChatApp = () => {
    const [messages, setMessages] = useState([]);
    const [input, setInput] = useState('');
    const [socket, setSocket] = useState(null);

    useEffect(() => {
        const newSocket = new WebSocket('wss://example.com/chat');
        newSocket.onmessage = (event) => {
            const newMessage = JSON.parse(event.data);
            setMessages((prev) => [...prev, newMessage]);
        };

        setSocket(newSocket);
        return () => newSocket.close();
    }, []);

    const sendMessage = () => {
        if (socket && input.trim()) {
            socket.send(JSON.stringify({ message: input, timestamp: Date.now() }));
            setInput('');
        }
    };

    return (
        <div>
            <h1>Chat Room</h1>
            <div className="chat-box">
                {messages.map((msg, index) => (
                    <div key={index} className="chat-message">
                        <span className="chat-timestamp">
                            {new Date(msg.timestamp).toLocaleTimeString()}
                        </span>
                        : {msg.message}
                    </div>
                ))}
            </div>
            <div className="chat-input">
                <input
                    type="text"
                    value={input}
                    onChange={(e) => setInput(e.target.value)}
                    placeholder="Type your message..."
                />
                <button onClick={sendMessage}>Send</button>
            </div>
        </div>
    );
};

export default ChatApp;

動作説明

  • メッセージ受信: WebSocketを使用して新しいメッセージをリアルタイムで受信します。
  • メッセージ送信: WebSocketを通じて新しいメッセージを送信します。
  • UI設計: メッセージ履歴を表示するchat-boxと入力フォームを分けています。

追加機能の提案

  • ユーザー名やアイコンの表示
  • メッセージの編集・削除機能
  • 未読メッセージ数の表示

まとめ


株価やチャットアプリは、ストリーミングデータを活用した典型的なリアルタイムアプリケーションの例です。これらを構築することで、ストリーミングデータの処理やUI設計のスキルを実践的に習得できます。次項では、これらの知識を応用した演習課題を紹介します。

演習: ストリーミングアプリの作成

演習の目的


これまで学んだ知識を実践するために、簡単なストリーミングアプリケーションを作成します。この演習では、リアルタイムデータの取得、状態管理、UI設計を統合的に学べます。


演習課題

課題内容


「リアルタイム温度モニタリングアプリ」を作成します。このアプリでは、センサー(シミュレーションされたWebSocketサーバー)から送られる温度データをリアルタイムで表示し、一定のしきい値を超えた場合に警告を表示します。


演習の要件

  1. リアルタイムデータの取得: WebSocketを使用して温度データを受信します。
  2. 状態管理: 温度データを状態として保持し、過去の温度履歴を表示します。
  3. UI設計:
  • 最新の温度を画面上部に表示。
  • 温度履歴を表形式で表示。
  • しきい値を超えた場合、警告を赤文字で表示。
  1. 追加機能:
  • 温度の単位(摂氏/華氏)を切り替える機能。
  • データ履歴をクリアするボタン。

コード例

import React, { useState, useEffect } from 'react';

const TemperatureMonitor = () => {
    const [temperature, setTemperature] = useState(null);
    const [history, setHistory] = useState([]);
    const [isCelsius, setIsCelsius] = useState(true);

    useEffect(() => {
        const socket = new WebSocket('ws://localhost:8080');

        socket.onmessage = (event) => {
            const temp = JSON.parse(event.data).temperature;
            setTemperature(temp);
            setHistory((prev) => [...prev, temp]);
        };

        return () => socket.close();
    }, []);

    const toggleUnit = () => {
        setIsCelsius((prev) => !prev);
    };

    const clearHistory = () => {
        setHistory([]);
    };

    const convertTemperature = (temp) => {
        return isCelsius ? temp : temp * 1.8 + 32;
    };

    const threshold = 30; // 温度のしきい値(摂氏)
    const displayTemperature = convertTemperature(temperature);

    return (
        <div>
            <h1>Real-time Temperature Monitor</h1>
            {temperature !== null && (
                <div>
                    <h2
                        style={{
                            color: temperature > threshold ? 'red' : 'black',
                        }}
                    >
                        Current Temperature: {displayTemperature.toFixed(1)}°{isCelsius ? 'C' : 'F'}
                    </h2>
                    <button onClick={toggleUnit}>
                        Switch to {isCelsius ? 'Fahrenheit' : 'Celsius'}
                    </button>
                </div>
            )}
            <div>
                <h3>Temperature History</h3>
                <table>
                    <thead>
                        <tr>
                            <th>#</th>
                            <th>Temperature (°{isCelsius ? 'C' : 'F'})</th>
                        </tr>
                    </thead>
                    <tbody>
                        {history.map((temp, index) => (
                            <tr key={index}>
                                <td>{index + 1}</td>
                                <td>{convertTemperature(temp).toFixed(1)}</td>
                            </tr>
                        ))}
                    </tbody>
                </table>
                <button onClick={clearHistory}>Clear History</button>
            </div>
        </div>
    );
};

export default TemperatureMonitor;

演習手順

  1. WebSocketサーバーを準備: シミュレーションとして、以下のNode.jsサーバーを使用します。
const WebSocket = require('ws');

const server = new WebSocket.Server({ port: 8080 });

server.on('connection', (socket) => {
    console.log('Client connected');
    setInterval(() => {
        const temperature = (Math.random() * 40).toFixed(1); // ランダムな温度データ
        socket.send(JSON.stringify({ temperature }));
    }, 1000);

    socket.on('close', () => console.log('Client disconnected'));
});
  1. Reactアプリケーションの作成: 上記コードをApp.jsにコピーし、ローカルで実行します。
  2. UIの改善: CSSを利用して、表やボタンのデザインを洗練させます。
  3. しきい値変更の実装(オプション): ユーザーがしきい値を調整できるスライダーを追加します。

学習成果


この演習を通じて、次のスキルを習得できます。

  • WebSocketを利用したリアルタイムデータの取得
  • Reactでの状態管理とコンポーネント設計
  • データの視覚化とインタラクティブなUI作成

次項では、今回の内容をまとめ、学んだことを振り返ります。

まとめ


本記事では、Reactを使ったリアルタイムデータ処理とストリーミング表示の方法について学びました。WebSocketを利用したデータ取得の基本概念から、効率的な状態管理、高パフォーマンスなUI設計、エラーハンドリング、再接続の実装まで、リアルタイムアプリケーションに必要なスキルを体系的に解説しました。

さらに、株価表示アプリやチャットアプリといった実践的な応用例を通じて、実用的な知識を深めるとともに、演習課題としてリアルタイム温度モニタリングアプリの作成に挑戦する機会を提供しました。

これらの知識とスキルを活用すれば、Reactを使ったリアルタイムアプリケーションの開発に自信を持って取り組むことができるはずです。次のステップとして、学んだ内容をさらに応用し、自分だけのユニークなリアルタイムアプリケーションを構築してみてください。

コメント

コメントする

目次
  1. Reactでのデータストリーミングの基本概念
    1. ストリーミングデータとは
    2. Reactの特徴とストリーミングデータ
    3. データ取得の一般的な手法
  2. WebSocketの導入と設定方法
    1. WebSocketとは
    2. WebSocketのセットアップ手順
    3. 必要な依存パッケージ
    4. WebSocket利用時の注意点
  3. サンプルコードで学ぶストリーミング処理
    1. Reactアプリケーションでのストリーミング実装
    2. コードのポイント解説
    3. 実行手順
    4. 動作確認
  4. 状態管理と効率的な更新の実現
    1. リアルタイム更新におけるReactの状態管理
    2. ストリーミングデータと状態の構造
    3. 再レンダリングの最適化
    4. 大規模データの効率的なレンダリング
    5. パフォーマンスを意識したUI更新
  5. エラーハンドリングと再接続の実装
    1. リアルタイムアプリケーションにおけるエラーハンドリングの重要性
    2. WebSocketにおけるエラーハンドリング
    3. 再接続の実装
    4. 接続の安定性を向上させるヒント
  6. 高パフォーマンスなUIの設計
    1. リアルタイムアプリケーションにおけるUI設計の課題
    2. レンダリング効率の向上
    3. ユーザーの視認性を高めるUI設計
    4. 負荷を軽減するための工夫
  7. 応用例: 株価やチャットアプリの構築
    1. リアルタイムデータの応用例
    2. 応用例1: 株価表示システム
    3. 応用例2: リアルタイムチャットアプリ
    4. まとめ
  8. 演習: ストリーミングアプリの作成
    1. 演習の目的
    2. 演習課題
    3. 演習の要件
    4. コード例
    5. 演習手順
    6. 学習成果
  9. まとめ