事件监听器中有多个状态更改,如何不批处理DOM更新?

时间:2020-08-19 06:42:48

标签: reactjs

我正在构建一个组件来测试不同算法的性能。该算法返回运行所花费的ms,这是我想显示的。 “ fastAlgorithm”大约需要半秒钟,而“ slowAlgorithm”大约需要5秒。

我的问题是,直到两种算法都完成后,UI才会重新呈现结果。我想在快速算法完成时立即显示结果,在慢速算法完成时显示结果。

我已经阅读了有关React批处理在重新渲染之前如何更新的信息,但是有什么方法可以改变这种行为?还是有更好的方法来组织我的组件来实现我想要的?

我正在使用反应16.13.1

这是我的组成部分:

import { useState } from 'react'
import { fastAlgorithm, slowAlgorithm } from '../utils/algorithms'

const PerformanceTest = () => {

  const [slowResult, setSlowResult] = useState(false)
  const [fastResult, setFastResult] = useState(false)

  const testPerformance = async () => {
    fastAlgorithm().then(result => {
      setFastResult(result)
    })
    slowAlgorithm().then(result => {
      setSlowResult(result)
    })
  }

  return (
    <div>
      <button onClick={testPerformance}>Run test!</button>
      <div>{fastResult}</div>
      <div>{slowResult}</div>
    </div>
  )
}

export default PerformanceTest

我在某处读到ReactDOM.flushSync()会在每次状态更改时触发重新渲染,但没有任何区别。这是我尝试过的:

const testPerformance = async () => {
  ReactDOM.flushSync(() =>
    fastAlgorithm().then(result => {
      setFastResult(result)
    })
  )
  ReactDOM.flushSync(() => 
    slowAlgorithm().then(result => {
      setSlowResult(result)
    })
  )
}

还有这个:

const testPerformance = async () => {
  fastAlgorithm().then(result => {
    ReactDOM.flushSync(() =>
      setFastResult(result)
    )
  })
  slowAlgorithm().then(result => {
    ReactDOM.flushSync(() =>
      setSlowResult(result)
    )
  })
}

我也尝试过重组算法,以便他们不使用Promises并尝试这样做,但是没有运气:

 const testPerformance = () => {
   setFastResult(fastAlgorithm())
   setSlowResult(slowAlgorithm())
 }

修改

正如 Sujoy Saha 在下面的评论中建议的那样,我使用setTimeout()将算法替换为简单的算法,并且一切正常。首先显示“快速”,然后两秒钟后显示“慢”。

但是,如果我做下面的代码这样的事情是行不通的。当较慢的功能完成时,“ Fast”和“ Slow”都会显示出来。有人知道确切的时间/方式如何在React中进行批量渲染以及如何避免这种情况吗?

export const slowAlgorithm  = () => {
  return new Promise((resolve, reject) => {
    const array = []
    for(let i = 0; i < 9000; i++) {
      for(let y = 0; y < 9000; y++) {
        array.push(y);
      }
    }
    resolve('slow')
  })
}

2 个答案:

答案 0 :(得分:0)

您最初的 PerfomanceTest 组件是正确的。组件将针对每个状态更改重新呈现。我认为问题出在您的算法中。请让我们知道您是如何在那儿退还诺言的。 请遵循以下代码段供您参考。

export const fastAlgorithm  = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('fast')
    }, 1000)
  })
}

export const slowAlgorithm  = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('slow')
    }, 3000)
  })
}

答案 1 :(得分:0)

您是否在主线程上同步运行算法?如果是这样,那可能是阻止React重新渲染的原因。您可能需要将它们移至worker threads

下面的内容大致基于this answer,减去了所有兼容性(假设您不需要IE支持):

cut -d\" \"

请注意,// Note that `args` must each be JSON serializable, // due to how we're creating the worker string. // If there are no arguments, that's fine too (`args` is then []) const asyncify = fn => (...args) => { const workerStr = ` const fn = ${fn.toString()} const start = new Date().valueOf() self.onmessage = () => { const returnVal = fn(${args.map(JSON.stringify).join(",")}) const executionTimeMs = new Date().valueOf() - start self.postMessage({ returnVal, executionTimeMs }) } ` const blob = new Blob([workerStr], { type: "application/javascript" }) const worker = new Worker(URL.createObjectURL(blob)) const promise = new Promise((resolve, reject) => { worker.onmessage = (result) => { resolve(result) worker.terminate() } worker.onerror = (err) => { reject(err) worker.terminate() } }) worker.postMessage(null) // in case we need it for React cleanup later promise.abort = worker.terminate.bind(worker) return promise } const slowAlgorithm = (outerIterations, innerIterations) => { const array = [] for (let i = 0; i < outerIterations; i++) { for (let y = 0; y < innerIterations; y++) { array.push(y) } } return array.length } const slowAlgorithmAsync = asyncify(slowAlgorithm) // rendering not blocked - just pretend this is React const render = x => document.write(`<pre>${JSON.stringify(x, null, 2)}</pre>`) slowAlgorithmAsync(9000, 9000) .then(({ data }) => render(data)) slowAlgorithmAsync(100, 100) .then(({ data }) => render(data))实际上是在此处的工作线程上下文中被fn编辑的,因此您需要确保代码受信任。假定您已经很乐意在主线程上运行它了。