I posted an earlier question about this, but have not had any luck finding a solution, and seeing behavior I don't understand. I've reworked the code a bit and will explain some of the things I tried.
The problem I want to display a list of prime numbers as they are generated. First of all, here's a test function that behaves correctly:
const testPrimes = (primes, setPrimes) => {
const tPrimes = [3, 5, 7];
for (const [i, p] of tPrimes.entries()) {
setTimeout(() => {
setPrimes((oldPrimes) =>
oldPrimes.concat(<Response {...{ prime: p, key: p }} />)
);
}, i * 1000);
}
};
And this is called like so:
export default ({ args, appState }) => {
const [primes, setPrimes] = useState([]);
useEffect(() => {
testPrimes(setPrimes);
}, []);
return (
<div>
<div className="row">{primes}</div>
</div>
);
};
This displays each prime in the list at one second intervals, which is the desired behavior.
In the "real" program, I use a generator function instead of an array:
const testPrimes3 = (args, setPrimes) => {
const gen = fPrimes({ args });
let i = 0;
for (const p of gen) {
console.log("foo");
setTimeout(() => {
setPrimes((oldPrimes) =>
oldPrimes.concat(<Response {...{ prime: p, key: p }} />)
);
}, 100);
}
};
The testPrime3
function is called similar to testPrime
above (see below);
When testPrime3 is called, I see 'foo' displayed 3 times in the console, with time intervals between each, but the list in React gets populated all at once when the generator completes, instead of incrementally for each prime yielded.
I've tried wrapping the setEffect call to the generator in a setTimeout, but that has no effect; here's the call with the setTimeout commented out:
useEffect(() => {
// setTimeout(() =>
testPrimes3(args, setPrimes);
// , 1000);
}, []);
Is there a better way to control the incremental rendering of elements in the list other than through a setState call?
update
It was suggested to not add an element to primes
, but render it in the component instead:
const testPrimes3 = (args, setPrimes) => {
const gen = fPrimes({ args });
let i = 0;
for (const p of gen) {
console.log("foo");
setTimeout(() => {
setPrimes((oldPrimes) => oldPrimes.concat(p));
}, 100);
}
};
[...]
return (
<div>
<div className="row">
{primes.map((p) => (
<Response {...{ prime: p, key: p }} />
))}
</div>
</div>
);
This has no effect.