react-three-fiber
react-three-fiber copied to clipboard
Handle exceptions in native Canvas renderFrame in React Error Boundary
I'm wrapping a @reat-three/fiber/native Canvas element in an ErrorBoundary, but it's not properly handling errors.
Here's a raw (minified) stack trace for an error in production:
TypeError: Cannot read property 'name' of undefined
at WebGLUniforms(/node_modules/three/build/three.cjs:19298:48)
at onFirstUse(/node_modules/three/build/three.cjs:20388:37)
at anonymous(/node_modules/three/build/three.cjs:20402:14)
at setProgram(/node_modules/three/build/three.cjs:30556:42)
at anonymous(/node_modules/three/build/three.cjs:29438:30)
at renderObject(/node_modules/three/build/three.cjs:30240:29)
at renderObjects(/node_modules/three/build/three.cjs:30209:18)
at renderScene(/node_modules/three/build/three.cjs:30070:49)
at anonymous(/node_modules/three/build/three.cjs:29888:16)
at anonymous(/node_modules/@react-three/fiber/native/dist/react-three-fiber-native.cjs.prod.js:248:22)
at render$1(/node_modules/@react-three/fiber/dist/index-f0ac6f0d.cjs.prod.js:1568:67)
at loop(/node_modules/@react-three/fiber/dist/index-f0ac6f0d.cjs.prod.js:1592:27)
at _callTimer(/node_modules/react-native/Libraries/Core/Timers/JSTimers.js:113:15)
at callTimers(/node_modules/react-native/Libraries/Core/Timers/JSTimers.js:359:17)
at apply(native)
at __callFunction(/node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:433:34)
at anonymous(/node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:113:26)
at __guard(/node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:368:11)
at callFunctionReturnFlushedQueue(/node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:112:17)
the specific react-three-fiber-native.cjs.prod.js line maps to this renderFrame function:
// Bind render to RN bridge
const context = state.gl.getContext() as ExpoWebGLRenderingContext
const renderFrame = state.gl.render.bind(state.gl)
state.gl.render = (scene: THREE.Scene, camera: THREE.Camera) => {
> renderFrame(scene, camera)
context.endFrameEXP()
}
Unfortunately because this is in a callback that's called by the three.js code, any exceptions are uncaught and crash the native app.
One suggestion would be to wrap it in a try/except and call the setError function:
// Bind render to RN bridge
const context = state.gl.getContext() as ExpoWebGLRenderingContext
const renderFrame = state.gl.render.bind(state.gl)
state.gl.render = (scene: THREE.Scene, camera: THREE.Camera) => {
+ try {
+ renderFrame(scene, camera)
+ context.endFrameEXP()
+ } catch (error) {
+ setError(error)
+ }
}
and this will get automatically thrown and therefore handled by a wrapping ErrorBoundary.
let me know if this sounds reasonable!
Possibly related: #3181
I think we have a general problem of errors not being propagated to the error boundary if thrown from a loop or the reconciler itself. We could use a correction here. #3181 is far simpler to fix, just more complex to test.
Do you have a suggestion for a catch-all solution that would work for both #3181 and this issue? I'd be happy to help test with my own project.