提问人:MervS 提问时间:5/19/2023 最后编辑:MervS 更新时间:5/30/2023 访问量:494
数据流在 React 客户端中不起作用
Streaming of data not working in React client
问:
我目前正在开发一个 React 应用程序,该应用程序需要响应流。为了简化该方案,假设我有一个每秒返回一个数字的终结点。为此,我正在使用 Flask:
def stream():
def generate():
import math, time
for i in range(10):
yield f'{i}\n'
time.sleep(1)
return generate(), {"Content-Type": "text/plain"}
在我的 React 组件中,单击按钮将触发以下功能:
const readStream = () => {
const xhr = new XMLHttpRequest();
xhr.seenBytes = 0;
xhr.open('GET', '/stream', true);
xhr.onreadystatechange = function(){
if (this.readyState === 3){
const data = xhr.response.substr(xhr.seenBytes)
console.log(data)
setNumber(data) // set state value that will be displayed elsewhere
xhr.seenBytes = xhr.responseText.length;
}
}
xhr.send();
}
出于某种原因,React 代码无法一次获取一个块,它只是一次转储所有数据。我已经验证了在 vanilla JS 中使用相同的 ajax 请求有效。我也尝试了不同的方法,包括使用,但到目前为止都没有奏效。fetch
axios
虽然我在这里使用,但我的实际用例需要并且我必须从该响应中获取我的数据。GET
POST
我错过了什么吗?
答:
您似乎面临着使用 XMLHttpRequest 在 React 应用程序中接收流数据的问题。您描述的问题表明,数据不是分块接收的,而是一次性接收的。
此行为的一个可能原因是 XMLHttpRequest 的 onreadystatechange 事件在流式处理过程中可能不会触发。相反,它会等到响应完成后再触发。若要解决此问题,可以修改代码以改用 xhr.onprogress 事件,该事件专门用于在接收数据时跟踪数据。
下面是使用 onprogress 事件的代码的更新版本:
const readStream = () => {
const xhr = new XMLHttpRequest();
xhr.open('GET', '/stream', true);
xhr.onprogress = function(event) {
const data = xhr.response.substr(xhr.seenBytes);
console.log(data);
setNumber(data); // set state value that will be displayed elsewhere
xhr.seenBytes = xhr.responseText.length;
};
xhr.send();
};
通过将 onreadystatechange 替换为 onprogress,您应该能够按预期以块形式接收流数据。
关于您提到的在实际用例中使用 POST,可以应用相同的方法。您需要相应地修改服务器端代码和客户端代码。
在服务器端 (Flask) 上,您将更改端点以接受 POST 请求:
@app.route('/stream', methods=['POST'])
def stream():
# Your streaming logic here
在客户端 (React) 上,您将更新 xhr.open 方法以指定 POST 请求:
xhr.open('POST', '/stream', true);
通过这些修改,您应该能够使用 XMLHttpRequest 和 GET 和 POST 请求来接收流数据。
注意:考虑使用更现代的方法,如 Fetch API 或 Axios 在 React 中发出 HTTP 请求,因为它们为处理流数据提供了更简单、更灵活的选项。
更新:
const readStream = () => {
const xhr = new XMLHttpRequest();
xhr.open('GET', '/stream', true);
xhr.onprogress = function(event) {
const data = xhr.response.substr(xhr.seenBytes);
setTimeout(() => {
setNumber(prevNumber => prevNumber + data); // concatenate the received chunk to the previous state value
}, 0);
xhr.seenBytes = xhr.responseText.length;
};
xhr.send();
};
在此更新的代码中,使用 setTimeout 函数的延迟为 0 毫秒。虽然这似乎有悖常理,但这种方法将状态更新推迟到事件循环的下一个时钟周期,允许 React 在收到每个块后渲染和更新 DOM。
通过以这种方式使用 setTimeout,您应该能够在 React 应用程序中实现所需的流式处理行为。
评论
onprogress
xhr
fetch
axios
Cache-control
no-transform
评论