我的 React 列表项组件不会一次呈现一个

IT技术 javascript reactjs
2021-05-24 11:17:07

我不再做很多 React 编程,所以我希望这个有一个简单的答案。

我有一个模拟生成器函数,它以 2 秒的间隔返回 3 个素数。当它们返回时,我将产生的每个素数插入到一个列表中。在返回所有 3 个素数之前,该列表不会呈现。我只附上相关代码。

function* testPrimes(addPrime) {
  const primes = [3, 5, 7];
  let time = Date.now();
  let i = 0;
  do {
    const nTime = Date.now();

    if (nTime - time > 2000) {
      yield primes[i++];
      time = nTime;
    }
  } while (i < primes.length);
}

在父组件中,我填充包含的列表(实际上只是 div):

  return (
    <div>
      <div>{appState === RUNNING ? "Running..." : "Stopped"}</div>
      <div className="row">
        {(() => {
          if (appState === RUNNING) {
            for (const prime of testPrimes()) {
              console.log("foo");
              primes.push(<Response {...{ prime, key: prime }} />);
            }
          }
        })()}
        {primes}
      </div>
    </div>
  );

我看到每隔一段时间打印出“foo”,但 <Response> 组件是一次性呈现的。

2个回答

由于 Javascript 是单线程的,因此 UI 渲染会被阻止,直到产生所有质数。setTimeout()改为用于非 UI 阻塞素数生成。并且您不应该在render()函数中生成素数,而是在componentDidMount()其他地方或其他地方生成素数,并通过更新状态让组件重新呈现。

您也可以使用 Pub-Sub 模式。useEffect.

// Check this library: https://www.npmjs.com/package/pubsub-js
import pubSub from 'pubsub-js';

function *generator() {
  // This yields primes. 
  // For eg:
  const primes = [2, 3, 5, 7, ...]
  let i = 0;
  while (true) {
    yield primes[i++];
  }
  // You can use your own logic get next prime. 
}


const generatorInstance = generator();

function publishPrimes(time = 2000) {
  const prime1 = generatorInstance.next().value;  
  const prime2 = generatorInstance.next().value;  
  const prime3 = generatorInstance.next().value;  

  // Publish 3 primes
  pubSub.publish('NEXT_PRIMES', [prime1, prime2, prime3]);

  // Use setTimeout to publish next 3 after 2 seconds 
  // instead of waiting for 2 seconds to complete using a
  // while loop, which is a **blocking** operation.
  setTimeout(() => {
    publishPrimes();
  }, time);
}

// Do the following inside a useEffect of a component
pubSub.subscribe('NEXT_PRIMES', (primes) => {
  setResponses([
    ...responses, 
    ...primes.map(p => <Response prime={p} />),
  ])
});