React Native + Stream Data: How to handle stream data like ChatGPT?
Tags: reactnative, chatgpt, stream, javascript
Published: 2024-02-23 12:44:08

**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](https://github.com/rgommezz/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

```py

"""
# 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())
```

### Step 2. Test API with your browser
- Open your browser
- Access link http://127.0.0.1:8000
- Open console and run code below

```js
(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)
    }

})()
```


![Result](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xsmmlqep8v4t9unzvjqh.png)


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

**Install libs**
```bash
npm i react-native-webview
```

**App.js**
```jsx
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>
  );
}

```


![Finally result](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/55hibfpefcbwjl3l4pri.png)