@react-three/fiber 9.7.0 → 9.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,7 +2,7 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var events = require('./events-b1bdeb1a.cjs.dev.js');
5
+ var events = require('./events-21d8cd92.cjs.dev.js');
6
6
  var React = require('react');
7
7
  var THREE = require('three');
8
8
  var useMeasure = require('react-use-measure');
@@ -78,6 +78,7 @@ function CanvasImpl({
78
78
  const divRef = React__namespace.useRef(null);
79
79
  React__namespace.useImperativeHandle(ref, () => canvasRef.current);
80
80
  const handlePointerMissed = events.useMutableCallback(onPointerMissed);
81
+ const pointerMissed = React__namespace.useCallback(event => handlePointerMissed.current == null ? void 0 : handlePointerMissed.current(event), [handlePointerMissed]);
81
82
  const [block, setBlock] = React__namespace.useState(false);
82
83
  const [error, setError] = React__namespace.useState(false);
83
84
 
@@ -86,46 +87,65 @@ function CanvasImpl({
86
87
  // Throw exception outwards if anything within canvas throws
87
88
  if (error) throw error;
88
89
  const root = React__namespace.useRef(null);
90
+
91
+ // Waits for an async renderer without hiding the canvas
92
+ const [gate, waitFor] = events.useGate();
93
+ const rootState = React__namespace.useRef(null);
94
+ const eventTarget = () => eventSource ? events.isRef(eventSource) ? eventSource.current : eventSource : divRef.current;
95
+
96
+ // Insertion effects survive Activity hiding and StrictMode effect replay. Only
97
+ // final removal releases the root, including removal while hidden from React 19.2
98
+ React__namespace.useInsertionEffect(() => {
99
+ return () => {
100
+ const current = root.current;
101
+ root.current = null;
102
+ current == null ? void 0 : current.unmount();
103
+ };
104
+ }, []);
89
105
  events.useIsomorphicLayoutEffect(() => {
90
106
  const canvas = canvasRef.current;
91
107
  if (containerRect.width > 0 && containerRect.height > 0 && canvas) {
92
108
  if (!root.current) root.current = events.createRoot(canvas);
93
- async function run() {
94
- await root.current.configure({
95
- gl,
96
- scene,
97
- events: events$1,
98
- shadows,
99
- linear,
100
- flat,
101
- legacy,
102
- orthographic,
103
- frameloop,
104
- dpr,
105
- performance,
106
- raycaster,
107
- camera,
108
- size: containerRect,
109
- // Pass mutable reference to onPointerMissed so it's free to update
110
- onPointerMissed: (...args) => handlePointerMissed.current == null ? void 0 : handlePointerMissed.current(...args),
111
- onCreated: state => {
112
- // Connect to event source
113
- state.events.connect == null ? void 0 : state.events.connect(eventSource ? events.isRef(eventSource) ? eventSource.current : eventSource : divRef.current);
114
- // Set up compute function
115
- if (eventPrefix) {
116
- state.setEvents({
117
- compute: (event, state) => {
118
- const x = event[eventPrefix + 'X'];
119
- const y = event[eventPrefix + 'Y'];
120
- state.pointer.set(x / state.size.width * 2 - 1, -(y / state.size.height) * 2 + 1);
121
- state.raycaster.setFromCamera(state.pointer, state.camera);
122
- }
123
- });
124
- }
125
- // Call onCreated callback
126
- onCreated == null ? void 0 : onCreated(state);
109
+ root.current.configure({
110
+ gl,
111
+ scene,
112
+ events: events$1,
113
+ shadows,
114
+ linear,
115
+ flat,
116
+ legacy,
117
+ orthographic,
118
+ frameloop,
119
+ dpr,
120
+ performance,
121
+ raycaster,
122
+ camera,
123
+ size: containerRect,
124
+ // Forwards to the latest onPointerMissed, so a new callback does not update the store
125
+ onPointerMissed: pointerMissed,
126
+ onCreated: state => {
127
+ var _eventTarget;
128
+ rootState.current = state;
129
+ // A ref to an ancestor is not attached yet. The effect below settles it
130
+ state.events.connect == null ? void 0 : state.events.connect((_eventTarget = eventTarget()) != null ? _eventTarget : divRef.current);
131
+ // Set up compute function
132
+ if (eventPrefix) {
133
+ state.setEvents({
134
+ compute: (event, state) => {
135
+ const x = event[eventPrefix + 'X'];
136
+ const y = event[eventPrefix + 'Y'];
137
+ state.pointer.set(x / state.size.width * 2 - 1, -(y / state.size.height) * 2 + 1);
138
+ state.raycaster.setFromCamera(state.pointer, state.camera);
139
+ }
140
+ });
127
141
  }
128
- });
142
+ // Call onCreated callback
143
+ onCreated == null ? void 0 : onCreated(state);
144
+ }
145
+ }).catch(setError);
146
+
147
+ // Pending re-runs this effect once the renderer lands. Rejected is reported by the catch
148
+ if (root.current.ready.status === 'fulfilled') {
129
149
  root.current.render( /*#__PURE__*/jsxRuntime.jsx(Bridge, {
130
150
  children: /*#__PURE__*/jsxRuntime.jsx(events.ErrorBoundary, {
131
151
  set: setError,
@@ -137,19 +157,34 @@ function CanvasImpl({
137
157
  })
138
158
  })
139
159
  }));
160
+ } else if (root.current.ready.status === 'pending') {
161
+ waitFor(root.current.ready);
140
162
  }
141
- run();
142
163
  }
143
164
  });
165
+
166
+ // Refs on ancestors attach after our layout effect
167
+ React__namespace.useEffect(() => {
168
+ var _rootState$current;
169
+ const state = (_rootState$current = rootState.current) == null ? void 0 : _rootState$current.get();
170
+ const target = eventTarget();
171
+ if (state && target && state.events.connected !== target) state.events.connect == null ? void 0 : state.events.connect(target);
172
+ });
173
+
174
+ // Before 19.2, React skips insertion cleanups in a subtree Suspense has hidden but keeps its
175
+ // passive effects connected. A canvas that left the document was removed rather than hidden
144
176
  React__namespace.useEffect(() => {
145
177
  const canvas = canvasRef.current;
146
- if (canvas) return () => events.unmountComponentAtNode(canvas);
178
+ return () => {
179
+ var _root$current;
180
+ if (!canvas.isConnected) (_root$current = root.current) == null ? void 0 : _root$current.unmount();
181
+ };
147
182
  }, []);
148
183
 
149
184
  // When the event source is not this div, we need to set pointer-events to none
150
185
  // Or else the canvas will block events from reaching the event source
151
186
  const pointerEvents = eventSource ? 'none' : 'auto';
152
- return /*#__PURE__*/jsxRuntime.jsx("div", {
187
+ return /*#__PURE__*/jsxRuntime.jsxs("div", {
153
188
  ref: divRef,
154
189
  style: {
155
190
  position: 'relative',
@@ -160,7 +195,7 @@ function CanvasImpl({
160
195
  ...style
161
196
  },
162
197
  ...props,
163
- children: /*#__PURE__*/jsxRuntime.jsx("div", {
198
+ children: [/*#__PURE__*/jsxRuntime.jsx("div", {
164
199
  ref: containerRef,
165
200
  style: {
166
201
  width: '100%',
@@ -173,7 +208,7 @@ function CanvasImpl({
173
208
  },
174
209
  children: fallback
175
210
  })
176
- })
211
+ }), gate]
177
212
  });
178
213
  }
179
214
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var events = require('./events-4c71f21f.cjs.prod.js');
5
+ var events = require('./events-a816cbab.cjs.prod.js');
6
6
  var React = require('react');
7
7
  var THREE = require('three');
8
8
  var useMeasure = require('react-use-measure');
@@ -78,6 +78,7 @@ function CanvasImpl({
78
78
  const divRef = React__namespace.useRef(null);
79
79
  React__namespace.useImperativeHandle(ref, () => canvasRef.current);
80
80
  const handlePointerMissed = events.useMutableCallback(onPointerMissed);
81
+ const pointerMissed = React__namespace.useCallback(event => handlePointerMissed.current == null ? void 0 : handlePointerMissed.current(event), [handlePointerMissed]);
81
82
  const [block, setBlock] = React__namespace.useState(false);
82
83
  const [error, setError] = React__namespace.useState(false);
83
84
 
@@ -86,46 +87,65 @@ function CanvasImpl({
86
87
  // Throw exception outwards if anything within canvas throws
87
88
  if (error) throw error;
88
89
  const root = React__namespace.useRef(null);
90
+
91
+ // Waits for an async renderer without hiding the canvas
92
+ const [gate, waitFor] = events.useGate();
93
+ const rootState = React__namespace.useRef(null);
94
+ const eventTarget = () => eventSource ? events.isRef(eventSource) ? eventSource.current : eventSource : divRef.current;
95
+
96
+ // Insertion effects survive Activity hiding and StrictMode effect replay. Only
97
+ // final removal releases the root, including removal while hidden from React 19.2
98
+ React__namespace.useInsertionEffect(() => {
99
+ return () => {
100
+ const current = root.current;
101
+ root.current = null;
102
+ current == null ? void 0 : current.unmount();
103
+ };
104
+ }, []);
89
105
  events.useIsomorphicLayoutEffect(() => {
90
106
  const canvas = canvasRef.current;
91
107
  if (containerRect.width > 0 && containerRect.height > 0 && canvas) {
92
108
  if (!root.current) root.current = events.createRoot(canvas);
93
- async function run() {
94
- await root.current.configure({
95
- gl,
96
- scene,
97
- events: events$1,
98
- shadows,
99
- linear,
100
- flat,
101
- legacy,
102
- orthographic,
103
- frameloop,
104
- dpr,
105
- performance,
106
- raycaster,
107
- camera,
108
- size: containerRect,
109
- // Pass mutable reference to onPointerMissed so it's free to update
110
- onPointerMissed: (...args) => handlePointerMissed.current == null ? void 0 : handlePointerMissed.current(...args),
111
- onCreated: state => {
112
- // Connect to event source
113
- state.events.connect == null ? void 0 : state.events.connect(eventSource ? events.isRef(eventSource) ? eventSource.current : eventSource : divRef.current);
114
- // Set up compute function
115
- if (eventPrefix) {
116
- state.setEvents({
117
- compute: (event, state) => {
118
- const x = event[eventPrefix + 'X'];
119
- const y = event[eventPrefix + 'Y'];
120
- state.pointer.set(x / state.size.width * 2 - 1, -(y / state.size.height) * 2 + 1);
121
- state.raycaster.setFromCamera(state.pointer, state.camera);
122
- }
123
- });
124
- }
125
- // Call onCreated callback
126
- onCreated == null ? void 0 : onCreated(state);
109
+ root.current.configure({
110
+ gl,
111
+ scene,
112
+ events: events$1,
113
+ shadows,
114
+ linear,
115
+ flat,
116
+ legacy,
117
+ orthographic,
118
+ frameloop,
119
+ dpr,
120
+ performance,
121
+ raycaster,
122
+ camera,
123
+ size: containerRect,
124
+ // Forwards to the latest onPointerMissed, so a new callback does not update the store
125
+ onPointerMissed: pointerMissed,
126
+ onCreated: state => {
127
+ var _eventTarget;
128
+ rootState.current = state;
129
+ // A ref to an ancestor is not attached yet. The effect below settles it
130
+ state.events.connect == null ? void 0 : state.events.connect((_eventTarget = eventTarget()) != null ? _eventTarget : divRef.current);
131
+ // Set up compute function
132
+ if (eventPrefix) {
133
+ state.setEvents({
134
+ compute: (event, state) => {
135
+ const x = event[eventPrefix + 'X'];
136
+ const y = event[eventPrefix + 'Y'];
137
+ state.pointer.set(x / state.size.width * 2 - 1, -(y / state.size.height) * 2 + 1);
138
+ state.raycaster.setFromCamera(state.pointer, state.camera);
139
+ }
140
+ });
127
141
  }
128
- });
142
+ // Call onCreated callback
143
+ onCreated == null ? void 0 : onCreated(state);
144
+ }
145
+ }).catch(setError);
146
+
147
+ // Pending re-runs this effect once the renderer lands. Rejected is reported by the catch
148
+ if (root.current.ready.status === 'fulfilled') {
129
149
  root.current.render( /*#__PURE__*/jsxRuntime.jsx(Bridge, {
130
150
  children: /*#__PURE__*/jsxRuntime.jsx(events.ErrorBoundary, {
131
151
  set: setError,
@@ -137,19 +157,34 @@ function CanvasImpl({
137
157
  })
138
158
  })
139
159
  }));
160
+ } else if (root.current.ready.status === 'pending') {
161
+ waitFor(root.current.ready);
140
162
  }
141
- run();
142
163
  }
143
164
  });
165
+
166
+ // Refs on ancestors attach after our layout effect
167
+ React__namespace.useEffect(() => {
168
+ var _rootState$current;
169
+ const state = (_rootState$current = rootState.current) == null ? void 0 : _rootState$current.get();
170
+ const target = eventTarget();
171
+ if (state && target && state.events.connected !== target) state.events.connect == null ? void 0 : state.events.connect(target);
172
+ });
173
+
174
+ // Before 19.2, React skips insertion cleanups in a subtree Suspense has hidden but keeps its
175
+ // passive effects connected. A canvas that left the document was removed rather than hidden
144
176
  React__namespace.useEffect(() => {
145
177
  const canvas = canvasRef.current;
146
- if (canvas) return () => events.unmountComponentAtNode(canvas);
178
+ return () => {
179
+ var _root$current;
180
+ if (!canvas.isConnected) (_root$current = root.current) == null ? void 0 : _root$current.unmount();
181
+ };
147
182
  }, []);
148
183
 
149
184
  // When the event source is not this div, we need to set pointer-events to none
150
185
  // Or else the canvas will block events from reaching the event source
151
186
  const pointerEvents = eventSource ? 'none' : 'auto';
152
- return /*#__PURE__*/jsxRuntime.jsx("div", {
187
+ return /*#__PURE__*/jsxRuntime.jsxs("div", {
153
188
  ref: divRef,
154
189
  style: {
155
190
  position: 'relative',
@@ -160,7 +195,7 @@ function CanvasImpl({
160
195
  ...style
161
196
  },
162
197
  ...props,
163
- children: /*#__PURE__*/jsxRuntime.jsx("div", {
198
+ children: [/*#__PURE__*/jsxRuntime.jsx("div", {
164
199
  ref: containerRef,
165
200
  style: {
166
201
  width: '100%',
@@ -173,7 +208,7 @@ function CanvasImpl({
173
208
  },
174
209
  children: fallback
175
210
  })
176
- })
211
+ }), gate]
177
212
  });
178
213
  }
179
214
 
@@ -1,10 +1,10 @@
1
- import { e as extend, u as useBridge, a as useMutableCallback, b as useIsomorphicLayoutEffect, c as createRoot, i as isRef, E as ErrorBoundary, B as Block, d as unmountComponentAtNode, f as createPointerEvents } from './events-156d8d12.esm.js';
2
- export { t as ReactThreeFiber, _ as _roots, x as act, k as addAfterEffect, j as addEffect, l as addTail, n as advance, s as applyProps, y as buildGraph, q as context, g as createEvents, o as createPortal, c as createRoot, w as dispose, f as events, e as extend, h as flushGlobalEffects, p as flushSync, v as getRootState, m as invalidate, r as reconciler, d as unmountComponentAtNode, D as useFrame, F as useGraph, z as useInstanceHandle, G as useLoader, A as useStore, C as useThree } from './events-156d8d12.esm.js';
1
+ import { e as extend, u as useBridge, a as useMutableCallback, b as useGate, c as useIsomorphicLayoutEffect, d as createRoot, E as ErrorBoundary, B as Block, f as createPointerEvents, i as isRef } from './events-9ce18a08.esm.js';
2
+ export { t as ReactThreeFiber, _ as _roots, y as act, k as addAfterEffect, j as addEffect, l as addTail, n as advance, v as applyProps, z as buildGraph, s as context, g as createEvents, q as createPortal, d as createRoot, x as dispose, f as events, e as extend, h as flushGlobalEffects, o as flushSync, w as getRootState, m as invalidate, r as reconciler, p as unmountComponentAtNode, F as useFrame, G as useGraph, A as useInstanceHandle, H as useLoader, C as useStore, D as useThree } from './events-9ce18a08.esm.js';
3
3
  import * as React from 'react';
4
4
  import * as THREE from 'three';
5
5
  import useMeasure from 'react-use-measure';
6
6
  import { FiberProvider } from 'its-fine';
7
- import { jsx } from 'react/jsx-runtime';
7
+ import { jsx, jsxs } from 'react/jsx-runtime';
8
8
  import 'zustand/traditional';
9
9
  import 'suspend-react';
10
10
  import 'scheduler';
@@ -51,6 +51,7 @@ function CanvasImpl({
51
51
  const divRef = React.useRef(null);
52
52
  React.useImperativeHandle(ref, () => canvasRef.current);
53
53
  const handlePointerMissed = useMutableCallback(onPointerMissed);
54
+ const pointerMissed = React.useCallback(event => handlePointerMissed.current == null ? void 0 : handlePointerMissed.current(event), [handlePointerMissed]);
54
55
  const [block, setBlock] = React.useState(false);
55
56
  const [error, setError] = React.useState(false);
56
57
 
@@ -59,46 +60,65 @@ function CanvasImpl({
59
60
  // Throw exception outwards if anything within canvas throws
60
61
  if (error) throw error;
61
62
  const root = React.useRef(null);
63
+
64
+ // Waits for an async renderer without hiding the canvas
65
+ const [gate, waitFor] = useGate();
66
+ const rootState = React.useRef(null);
67
+ const eventTarget = () => eventSource ? isRef(eventSource) ? eventSource.current : eventSource : divRef.current;
68
+
69
+ // Insertion effects survive Activity hiding and StrictMode effect replay. Only
70
+ // final removal releases the root, including removal while hidden from React 19.2
71
+ React.useInsertionEffect(() => {
72
+ return () => {
73
+ const current = root.current;
74
+ root.current = null;
75
+ current == null ? void 0 : current.unmount();
76
+ };
77
+ }, []);
62
78
  useIsomorphicLayoutEffect(() => {
63
79
  const canvas = canvasRef.current;
64
80
  if (containerRect.width > 0 && containerRect.height > 0 && canvas) {
65
81
  if (!root.current) root.current = createRoot(canvas);
66
- async function run() {
67
- await root.current.configure({
68
- gl,
69
- scene,
70
- events,
71
- shadows,
72
- linear,
73
- flat,
74
- legacy,
75
- orthographic,
76
- frameloop,
77
- dpr,
78
- performance,
79
- raycaster,
80
- camera,
81
- size: containerRect,
82
- // Pass mutable reference to onPointerMissed so it's free to update
83
- onPointerMissed: (...args) => handlePointerMissed.current == null ? void 0 : handlePointerMissed.current(...args),
84
- onCreated: state => {
85
- // Connect to event source
86
- state.events.connect == null ? void 0 : state.events.connect(eventSource ? isRef(eventSource) ? eventSource.current : eventSource : divRef.current);
87
- // Set up compute function
88
- if (eventPrefix) {
89
- state.setEvents({
90
- compute: (event, state) => {
91
- const x = event[eventPrefix + 'X'];
92
- const y = event[eventPrefix + 'Y'];
93
- state.pointer.set(x / state.size.width * 2 - 1, -(y / state.size.height) * 2 + 1);
94
- state.raycaster.setFromCamera(state.pointer, state.camera);
95
- }
96
- });
97
- }
98
- // Call onCreated callback
99
- onCreated == null ? void 0 : onCreated(state);
82
+ root.current.configure({
83
+ gl,
84
+ scene,
85
+ events,
86
+ shadows,
87
+ linear,
88
+ flat,
89
+ legacy,
90
+ orthographic,
91
+ frameloop,
92
+ dpr,
93
+ performance,
94
+ raycaster,
95
+ camera,
96
+ size: containerRect,
97
+ // Forwards to the latest onPointerMissed, so a new callback does not update the store
98
+ onPointerMissed: pointerMissed,
99
+ onCreated: state => {
100
+ var _eventTarget;
101
+ rootState.current = state;
102
+ // A ref to an ancestor is not attached yet. The effect below settles it
103
+ state.events.connect == null ? void 0 : state.events.connect((_eventTarget = eventTarget()) != null ? _eventTarget : divRef.current);
104
+ // Set up compute function
105
+ if (eventPrefix) {
106
+ state.setEvents({
107
+ compute: (event, state) => {
108
+ const x = event[eventPrefix + 'X'];
109
+ const y = event[eventPrefix + 'Y'];
110
+ state.pointer.set(x / state.size.width * 2 - 1, -(y / state.size.height) * 2 + 1);
111
+ state.raycaster.setFromCamera(state.pointer, state.camera);
112
+ }
113
+ });
100
114
  }
101
- });
115
+ // Call onCreated callback
116
+ onCreated == null ? void 0 : onCreated(state);
117
+ }
118
+ }).catch(setError);
119
+
120
+ // Pending re-runs this effect once the renderer lands. Rejected is reported by the catch
121
+ if (root.current.ready.status === 'fulfilled') {
102
122
  root.current.render( /*#__PURE__*/jsx(Bridge, {
103
123
  children: /*#__PURE__*/jsx(ErrorBoundary, {
104
124
  set: setError,
@@ -110,19 +130,34 @@ function CanvasImpl({
110
130
  })
111
131
  })
112
132
  }));
133
+ } else if (root.current.ready.status === 'pending') {
134
+ waitFor(root.current.ready);
113
135
  }
114
- run();
115
136
  }
116
137
  });
138
+
139
+ // Refs on ancestors attach after our layout effect
140
+ React.useEffect(() => {
141
+ var _rootState$current;
142
+ const state = (_rootState$current = rootState.current) == null ? void 0 : _rootState$current.get();
143
+ const target = eventTarget();
144
+ if (state && target && state.events.connected !== target) state.events.connect == null ? void 0 : state.events.connect(target);
145
+ });
146
+
147
+ // Before 19.2, React skips insertion cleanups in a subtree Suspense has hidden but keeps its
148
+ // passive effects connected. A canvas that left the document was removed rather than hidden
117
149
  React.useEffect(() => {
118
150
  const canvas = canvasRef.current;
119
- if (canvas) return () => unmountComponentAtNode(canvas);
151
+ return () => {
152
+ var _root$current;
153
+ if (!canvas.isConnected) (_root$current = root.current) == null ? void 0 : _root$current.unmount();
154
+ };
120
155
  }, []);
121
156
 
122
157
  // When the event source is not this div, we need to set pointer-events to none
123
158
  // Or else the canvas will block events from reaching the event source
124
159
  const pointerEvents = eventSource ? 'none' : 'auto';
125
- return /*#__PURE__*/jsx("div", {
160
+ return /*#__PURE__*/jsxs("div", {
126
161
  ref: divRef,
127
162
  style: {
128
163
  position: 'relative',
@@ -133,7 +168,7 @@ function CanvasImpl({
133
168
  ...style
134
169
  },
135
170
  ...props,
136
- children: /*#__PURE__*/jsx("div", {
171
+ children: [/*#__PURE__*/jsx("div", {
137
172
  ref: containerRef,
138
173
  style: {
139
174
  width: '100%',
@@ -146,7 +181,7 @@ function CanvasImpl({
146
181
  },
147
182
  children: fallback
148
183
  })
149
- })
184
+ }), gate]
150
185
  });
151
186
  }
152
187
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var events = require('../../dist/events-b1bdeb1a.cjs.dev.js');
5
+ var events = require('../../dist/events-21d8cd92.cjs.dev.js');
6
6
  var React = require('react');
7
7
  var THREE = require('three');
8
8
  var reactNative = require('react-native');
@@ -80,6 +80,7 @@ function CanvasImpl({
80
80
  const [bind, setBind] = React__namespace.useState();
81
81
  React__namespace.useImperativeHandle(ref, () => viewRef.current);
82
82
  const handlePointerMissed = events.useMutableCallback(onPointerMissed);
83
+ const pointerMissed = React__namespace.useCallback(event => handlePointerMissed.current == null ? void 0 : handlePointerMissed.current(event), [handlePointerMissed]);
83
84
  const [block, setBlock] = React__namespace.useState(false);
84
85
  const [error, setError] = React__namespace.useState(undefined);
85
86
 
@@ -190,45 +191,50 @@ function CanvasImpl({
190
191
  });
191
192
  setBind(responder.panHandlers);
192
193
  }, []);
194
+
195
+ // Waits for an async renderer without hiding the view
196
+ const [gate, waitFor] = events.useGate();
193
197
  events.useIsomorphicLayoutEffect(() => {
194
198
  if (root.current && width > 0 && height > 0) {
195
- async function run() {
196
- await root.current.configure({
197
- gl,
198
- events: events$1,
199
- shadows,
200
- linear,
201
- flat,
202
- legacy,
203
- orthographic,
204
- frameloop,
205
- performance,
206
- raycaster,
207
- camera,
208
- scene,
209
- // expo-gl can only render at native dpr/resolution
210
- // https://github.com/expo/expo-three/issues/39
211
- dpr: reactNative.PixelRatio.get(),
212
- size: {
213
- width,
214
- height,
215
- top,
216
- left
217
- },
218
- // Pass mutable reference to onPointerMissed so it's free to update
219
- onPointerMissed: (...args) => handlePointerMissed.current == null ? void 0 : handlePointerMissed.current(...args),
220
- // Overwrite onCreated to apply RN bindings
221
- onCreated: state => {
222
- // Bind render to RN bridge
223
- const context = state.gl.getContext();
224
- const renderFrame = state.gl.render.bind(state.gl);
225
- state.gl.render = (scene, camera) => {
226
- renderFrame(scene, camera);
227
- context.endFrameEXP();
228
- };
229
- return onCreated == null ? void 0 : onCreated(state);
230
- }
231
- });
199
+ root.current.configure({
200
+ gl,
201
+ events: events$1,
202
+ shadows,
203
+ linear,
204
+ flat,
205
+ legacy,
206
+ orthographic,
207
+ frameloop,
208
+ performance,
209
+ raycaster,
210
+ camera,
211
+ scene,
212
+ // expo-gl can only render at native dpr/resolution
213
+ // https://github.com/expo/expo-three/issues/39
214
+ dpr: reactNative.PixelRatio.get(),
215
+ size: {
216
+ width,
217
+ height,
218
+ top,
219
+ left
220
+ },
221
+ // Forwards to the latest onPointerMissed, so a new callback does not update the store
222
+ onPointerMissed: pointerMissed,
223
+ // Overwrite onCreated to apply RN bindings
224
+ onCreated: state => {
225
+ // Bind render to RN bridge
226
+ const context = state.gl.getContext();
227
+ const renderFrame = state.gl.render.bind(state.gl);
228
+ state.gl.render = (scene, camera) => {
229
+ renderFrame(scene, camera);
230
+ context.endFrameEXP();
231
+ };
232
+ return onCreated == null ? void 0 : onCreated(state);
233
+ }
234
+ }).catch(setError);
235
+
236
+ // Pending re-runs this effect once the renderer lands. Rejected is reported by the catch
237
+ if (root.current.ready.status === 'fulfilled') {
232
238
  root.current.render( /*#__PURE__*/jsxRuntime.jsx(Bridge, {
233
239
  children: /*#__PURE__*/jsxRuntime.jsx(events.ErrorBoundary, {
234
240
  set: setError,
@@ -240,8 +246,9 @@ function CanvasImpl({
240
246
  })
241
247
  })
242
248
  }));
249
+ } else if (root.current.ready.status === 'pending') {
250
+ waitFor(root.current.ready);
243
251
  }
244
- run();
245
252
  }
246
253
  });
247
254
  React__namespace.useEffect(() => {
@@ -265,7 +272,7 @@ function CanvasImpl({
265
272
  style: reactNative.StyleSheet.absoluteFill,
266
273
  pointerEvents: pointerEvents,
267
274
  ...bind
268
- })]
275
+ }), gate]
269
276
  });
270
277
  }
271
278