【问题标题】:How to setState in a onTick event that ticks every millisecond如何在每毫秒滴答的 onTick 事件中设置状态
【发布时间】:2021-05-09 04:26:10
【问题描述】:

我正在尝试在 onTick 事件中为时钟设置状态。

 <Viewer>
   <Clock
        startTime={start.clone()}
        stopTime={stop.clone()}
        currentTime={start.clone()}
        multiplier={50}
        onTick={_.throttle(handleValue, 1000)} // this thing ticks every millisecond
      />
    <Entity
          ref={ref} // here is the ref to get the value I want to set state with
          position={positionProperty}
          tracked
          selected
          model={{ uri: model, minimumPixelSize: 100, maximumScale: 100.0 }}
          availability={
            new TimeIntervalCollection([
              new TimeInterval({ start: start, stop: stop }),
            ])
          }
        />
 </Viewer>

这里是handleValue函数。

  const handleValue = (clock) => {
//setting the state here ( I want to display the chaning the value over time) 
  setHeadingValue(ref.current.cesiumElement._properties._heading.getValue(clock.currentTime));
    }
  };

问题是它看起来像是试图一遍又一遍地重新渲染,这会冻结应用程序。 由于 setState 的性质,这种行为是有意义的。但我觉得有一个答案在逃避我。 我可以对我能做什么有一些了解吗?我没主意了。 我正在使用 Resium(一个反应库),现在我正在使用 .getElementByID() 设置值并附加到 dom.. 这首先会失败使用 react...

这里是一个代码沙箱:https://codesandbox.io/s/resium-cesium-context-forked-bpjuw?file=/src/ViewerComponent.js

有些元素没有显示,因为我们需要一个令牌,但这不会影响我正在寻找的功能。只需打开代码沙箱的控制台,进入ViewerComponent.js

感谢您的帮助

【问题讨论】:

  • 您想要实现什么行为?我的意思是你想设置值而不是重新渲染组件?但是您想如何使用该值?
  • 你是说这个Clock 组件实现了 some 间隔有 1ms 的延迟,这是太多的更新,所以你试图限制这个回调?大多数浏览器强制执行 4 毫秒的最小间隔/计时器延迟,并且 React 尝试以 60 帧/秒的速度运行 UI,这大约是每个渲染周期 17 毫秒,因此尝试比这更快的操作基本上是浪费。如果您正在运行时钟组件,为什么不使用与您关心的最小间隔相匹配的间隔延迟,这似乎是 1 秒限制?
  • 谢谢@DrewReese!用于反应的铯库称为 Resium。他们是提供时钟组件的人:resium.darwineducation.com/components/Clock 我需要使用该时钟中的 onTick 事件来获取时间流逝的值。我试图在 UI 中显示一个随着时间的推移而变化的属性,比如正在行驶的汽车和当前速度。我想在状态中保存该速度,但如果我在 onTick 事件中设置状态,整个 Cesium 组件会尝试重新渲染。我试过油门,但铯地图仍然冻结:(
  • 哇,这是一个非常酷的库。感谢您提供额外的详细信息。从文档中有很多内容需要消化。认为您可以创建并提供给我们一个running 代码框来重现我们可以实时调试的问题?
  • 谁在渲染 ?需要概述谁在渲染谁,也就是组件层次结构。

标签: javascript reactjs cesium


【解决方案1】:

正如我所见,这里的问题不在于库,而在于管理计算和可视化的方式。

正如少数人已经提到的,对于 UI,用户不需要超过 60fps,但对于进程,有时我们需要更多。

所以解决办法就是把处理和可视化分开。

为了更通用,这里是一个纯 JavaScript 的例子:

// Here it may be some component 
const clockView = document.getElementById('speedyClock')

let state = null
// This function mimicking dispatch on state update 
function setState(val) {
  state = val
  clockView.innerHTML = state
} 

const FPS = 30


// Any state out of visualization scope
// Not the React state!!!
let history = []

let nextUiUpdate = Date.now()+(1000/FPS) 

/// Super speedy process
setInterval(function() {
  history.push(Math.random())
  const now = Date.now()
  
  // Update according to visual frame rate
  if(now >= nextUiUpdate) {
    // Prepare visual updates
    setState(JSON.stringify({count: history.length, history}, null, 2))
    history = []    
    nextUiUpdate = Date.now()+(1000/FPS)
  }
}, 1)
&lt;pre id="speedyClock"&gt;&lt;/pre&gt;

【讨论】:

  • 感谢奥列格的回答!您介意使用我的代码框并留下一些 cmets 或一个示例来说明我对 cesium 的特殊情况吗?我想我明白了一般的想法,但我的代码框上的一个例子真的很有帮助:)
  • 只需将我的setInterval 中的功能复制到您的handleValue 中即可。您可以将多个值收集到某个全局变量中(不是反应状态)。如果需要刷新,只需 setState 提供最新的或收集到的值的摘要。
  • 谢谢!我会尝试一下,看看这是否能解决我的问题! :D
【解决方案2】:

我建议使用requestAnimationFrame,而不是setInterval,因为每次屏幕刷新都重新渲染 React 树是没有意义的。

【讨论】:

    【解决方案3】:

    通过结合使用shouldComponentUpdatesetState,您可以有效地setState 并防止不必要的渲染。

    This is my codesandbox url to show it.

    您可以随心所欲地访问您想要的值,但我们需要使用shouldComponentUpdate 函数防止每毫秒 1 次渲染。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-08-28
      • 2017-12-16
      • 1970-01-01
      • 2011-04-05
      • 1970-01-01
      • 2020-06-05
      • 2022-01-04
      相关资源
      最近更新 更多