I've been playing with using async generators in Typescript React apps.
I created the following component:
import React, {useEffect, useState} from 'react'
const delay = (ms : number) => new Promise(resolve => setTimeout(resolve, ms))
const fetchData = async (count : number, url: string) => {
await delay(1000);
const title : string = await fetch(`${url}/${count}`).then(response => response.json()).then(data => data.title)
return title;
}
const asyncGenerator = async function* () {
let count : number = 1;
const url : string = 'http://jsonplaceholder.typicode.com/posts';
while(true) {
yield fetchData(count, url)
count++;
}
}
const UseEffectComponent = () => {
const [titles, setTitles] = useState<string[]>([])
useEffect(() => {
(async () => {
for await (const title of asyncGenerator()) {
setTitles(t => [...t, title])
}
})();
}, [])
return (
<div>
<div>
{titles.map(t => <p>{t}</p>)}
</div>
</div>
)
}
export default UseEffectComponent;
Essentially, I've created a stream of post titles, which are supplied by the asyncGenerator
function, which will fetch data from the placeholder API every second by calling fetchData
which uses delay
to introduce an artificial 1000ms
delay.
I'm curious what is the best way to read this data from the stream in the component itself. My current implementation using useEffect
:
useEffect(() => {
(async () => {
for await (const title of asyncGenerator()) {
setTitles(t => [...t, title])
}
})();
has no cleanup code - meaning this stream will continue to supply data even when the component is unmounted. I'm not sure what the best way to fix this would be though. Can I exit this for of
loop when the component unmounts? Or is there a better way to consume data from my stream?
You can add isMounted
to check component is unmounted or not.
useEffect(() => {
let isMounted = true;
(async () => {
for await (const title of asyncGenerator()) {
if(!isMounted){
break;
}
setTitles(t => [...t, title])
}
})();
return () => {
isMounted = false;
};
}, [])