reearth / resium

React components for 🌏 Cesium
https://resium.reearth.io
MIT License
704 stars 130 forks source link

ref.current && ref.current.cesiumElement may always be undefined in docs #619

Open scottyob opened 11 months ago

scottyob commented 11 months ago

I've found ref.current && ref.current.cesiumElement may always be undefined in https://resium.reearth.io/guide#way-1-useref-hooks-function-component

Looks like it could be related to https://medium.com/@teh_builder/ref-objects-inside-useeffect-hooks-eb7c15198780 ?

gensmusic commented 10 months ago

got the same issue

djeemy2014 commented 10 months ago

Identical problem. I also noticed that when the server is restarted, a change is made and the problem goes away, but as soon as the page is refreshed, the problem appears again.

I noticed that when executing part of the code, console.log-0 shows that everything exists, and console.log - 1 displays undifind. I can assume that there is some kind of delay in processing... I’m still deciding how to get around this feature.

console.log(0, this.ref.cesiumElement) if (this.ref.current && this.ref.current.cesiumElement) { console.log(1, this.ref.current.cesiumElement)}

djeemy2014 commented 10 months ago

So. I tried to set the delay to 1 ms and it worked. I assume that when launched synchronously, it does not have time to work out the render function or runs it several times and useEffect thinks that it can work, but in fact useRef is not yet defined. Therefore, the condition is not met and the function does not work.

Now the question. Is it possible to do something asynchronously or set a wait in useEffect for the condition to be met?

import { useEffect, useRef } from "react"; import { Viewer } from "resium";

const ExampleComponent = () => { const ref = useRef(null);

useEffect(() => { console.log(ref.current.cesiumElement) setTimeout(()=>{ if (ref.current && ref.current.cesiumElement) { console.log(ref.current.cesiumElement) // ref.current.cesiumElement is Cesium's Viewer // DO SOMETHING } },1)

}, []);

return ; }; export default ExampleComponent

The second option is to write a similar function for testing. it will return a promise with a working link or an error.

async function testCesiumElemet(ref,i=0){ return new Promise((resolve, reject)=>{ //console.log() if (ref.current?.cesiumElement) { resolve (ref) } else if (i<500){ i++; setTimeout(()=>{ testCesiumElemet(ref, i).then(resolve).catch(reject) },10) }else { reject (new Error(Waiting more than ${500*10/1000} seconds.)) } })

}

sebastianmattar commented 10 months ago

I think I got the same problem. Rolling back to version 1.16.1 as a workaround fixed it for me.

djeemy2014 commented 9 months ago

After reading several articles and using the forum, I came to the conclusion that the problem is the lack of checks during the first rendering and the lack of waiting for React to respond. The checks that I described earlier are redundant since they are built into the useRef and creatRef code by default. There is probably a bug in the resium code associated with the transition of cesium to asynchronous functions.

Because of this, I also have two windows rendering at the same time during the first launch.

MrShtrahman commented 5 months ago

I've also encountered this issue before.

I had to use ref.current?.cesiumElement.clock which worked fine until upgrading to 1.17.2.

What solved this issue was actually making also cesiumElement itself nullable, meaning changing to ref.current?.cesiumElement?.clock.

Seems quite funny, but this conditional rendering actually gave the component the desired "re-render" which made the ref defined again.