DEV Community

Nguyễn Hữu Hiếu
Nguyễn Hữu Hiếu

Posted on • Edited on

React Native + Stream Data: How to handle stream data like ChatGPT?

Question: I want to create a chat UI that receives token by token like ChatGPT (stream data). But React Native does not support stream response. How to do it?

Ideal: we use react-native-webview to invoke API that can receive stream data
Thanks to the author of the react-native-chatgpt library

Step by step

  1. Create API that simulates stream data
  2. Test API with your browser
  3. Create react-native app that receives stream data like ChatGPT

Step 1. Create API that simulates stream data




"""
# filename: main.py
# To install
pip install fastapi
pip install "uvicorn[standard]"


# To run
uvicorn main:app --reload
"""
import asyncio
from fastapi import FastAPI
from starlette.responses import StreamingResponse
from time import time

app = FastAPI()


@app.get("/")
def read_root():
    return "Test stream"


async def generate_data():
    for i in range(5):
        yield f"message {i}\n"
        await asyncio.sleep(1)


@app.get("/stream")
def stream():
    return StreamingResponse(generate_data())


Enter fullscreen mode Exit fullscreen mode

Step 2. Test API with your browser



(async()=>{
    const response = await fetch('http://127.0.0.1:8000/stream', {
        method: 'GET',
        responseType: 'stream',
    });

    async function *streamAsyncIterable(stream) {
        const reader = stream.getReader()
        try {
            while (true) {
                const {done, value} = await reader.read()
                if (done) {
                    return
                }
                yield value
            }
        } finally {
            reader.releaseLock()
        }
    }

    for await(const chunk of streamAsyncIterable(response?.body)) {
        const str = new TextDecoder().decode(chunk);
        console.log(new Date().toISOString(), str)
    }

})()


Enter fullscreen mode Exit fullscreen mode

Result

Step 3. Create react-native app that receives stream data like ChatGPT

Install libs



npm i react-native-webview


Enter fullscreen mode Exit fullscreen mode

App.js



import React, { useRef, useState } from "react";
import { StatusBar } from "expo-status-bar";
import { Button, StyleSheet, Text, View } from "react-native";
import { WebView } from "react-native-webview";

export default function App() {
  const [message, setMessage] = useState("");
  const webviewRef = useRef();
  const getStreamData = () => {
    setMessage("");

    const injectScript = `
    (async()=>{
      const postMessage = window.ReactNativeWebView.postMessage;
      const response = await fetch('http://127.0.0.1:8000/stream', {
          method: 'GET',
          responseType: 'stream',
      });

      async function *streamAsyncIterable(stream) {
          const reader = stream.getReader()
          try {
              while (true) {
                  const {done, value} = await reader.read()
                  if (done) {
                      return
                  }
                  yield value
              }
          } finally {
              reader.releaseLock()
          }
      }

      for await(const chunk of streamAsyncIterable(response?.body)) {
          const str = new TextDecoder().decode(chunk);
          postMessage(new Date().toLocaleTimeString() + " " + str);
      }

    })()
    `;
    webviewRef?.current?.injectJavaScript(injectScript);
  };
  return (
    <View
      style={{
        flex: 1,
        backgroundColor: "#d2d2d2",
        textAlign: "center",
        paddingVertical: 120,
      }}
    >
      <WebView
        ref={webviewRef}
        style={{
          height: 80,
          width: "100%",
        }}
        source={{ uri: "http://127.0.0.1:8000/" }}
        onMessage={(event) => {
          const data = event.nativeEvent.data;
          setMessage((prev) => prev + data);
        }}
      />

      <View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
        <Text>{message}</Text>
        <Button title="Press me" onPress={getStreamData}></Button>
      </View>
    </View>
  );
}



Enter fullscreen mode Exit fullscreen mode

Finally result

Top comments (5)

Collapse
 
ekimcem profile image
Ekim Cem Ülger

Is there anyway to do it without webview?

Collapse
 
toannthblab profile image
toannt-hblab

You should go straight with webview.
Cause later we need to use some format engine line Mathjax.
Webview is good approach for now :D

Also. React native does not support streaming response.
You need to install the community customization for that.

Collapse
 
ekimcem profile image
Ekim Cem Ülger

Yes, you’re right, but using a web-view feels really pointless. It not only takes away the native feel of the app, but also doesn’t seem like a practical approach at all. Especially when there’s such a hype around AI in recent times, it’s hard to believe that React Native hasn’t addressed this yet…

Thread Thread
 
trangcongthanh profile image
Thành Trang

If you're using expo, the SDK 52 is support fetch streaming. But the mathjax or katex are still the problem.

Thread Thread
 
ekimcem profile image
Ekim Cem Ülger

Wow thats perfect, at least there is a step :D