Search code examples
javascriptreactjscesiumjs

How to setState in a onTick event that ticks every millisecond


I'm trying to set a state in an onTick event for a clock.

 <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>

Here is the handleValue function.

  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));
    }
  };

The problem is it looks like it tries to re-render over and over which freezes the app. Due to the nature of setState, this behavior makes sense. But I feel like there is an answer that's escaping me. May I have some insight as to what I could do? I'm out of ideas. I'm using Resium ( a react library) and right now I'm setting the value using .getElementByID() and appending to the dom.. which defeats using react in the first place...

Here is a code sandbox: https://codesandbox.io/s/resium-cesium-context-forked-bpjuw?file=/src/ViewerComponent.js

Some elements are not showing because we need a token, but that does not affect the functionality I'm looking for. Just open the console of the code sandbox and go to the ViewerComponent.js

thank you for your help


Solution

  • As I see, the problem here not in the library, but in a way of managing calculation and visualization.

    As few people already mentioned, for UI, user don't need more than 60fps, but for process sometimes we need more.

    So the solution is to separate processing from visualization.

    To be more generic, here is an example in pure 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)
    <pre id="speedyClock"></pre>