@react-three/fiber 10.0.0-alpha.3 → 10.0.0-alpha.5

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.
package/dist/legacy.mjs CHANGED
@@ -11,6 +11,7 @@ export { Scheduler, getScheduler } from '@pmndrs/scheduler';
11
11
  import Tb, { unstable_getCurrentPriorityLevel, unstable_IdlePriority, unstable_LowPriority, unstable_NormalPriority, unstable_UserBlockingPriority, unstable_ImmediatePriority, unstable_scheduleCallback } from 'scheduler';
12
12
  import { GroundedSkybox } from 'three/examples/jsm/objects/GroundedSkybox.js';
13
13
  import { HDRLoader } from 'three/examples/jsm/loaders/HDRLoader.js';
14
+ import { HDRCubeTextureLoader } from 'three/examples/jsm/loaders/HDRCubeTextureLoader.js';
14
15
  import { EXRLoader } from 'three/examples/jsm/loaders/EXRLoader.js';
15
16
  import { UltraHDRLoader } from 'three/examples/jsm/loaders/UltraHDRLoader.js';
16
17
  import { GainMapLoader } from '@monogrid/gainmap-js';
@@ -30,16 +31,14 @@ function _mergeNamespaces(n, m) {
30
31
 
31
32
  const R3F_BUILD_LEGACY = true;
32
33
  const R3F_BUILD_WEBGPU = false;
33
- const Inspector = class Inspector2 {
34
- constructor() {
35
- throw new Error("Inspector is not available in legacy builds. Use @react-three/fiber/webgpu instead.");
36
- }
37
- };
38
- const WebGPURenderer = class WebGPURenderer2 {
34
+ async function loadInspector() {
35
+ throw new Error("Inspector is not available in legacy builds. Use @react-three/fiber/webgpu instead.");
36
+ }
37
+ class WebGPURenderer {
39
38
  constructor() {
40
39
  throw new Error("WebGPURenderer is not available in legacy builds. Use @react-three/fiber/webgpu instead.");
41
40
  }
42
- };
41
+ }
43
42
  const RenderTarget = null;
44
43
  const CubeRenderTarget = null;
45
44
 
@@ -47,12 +46,12 @@ const THREE = /*#__PURE__*/_mergeNamespaces({
47
46
  __proto__: null,
48
47
  CubeRenderTarget: CubeRenderTarget,
49
48
  CubeRenderTargetCompat: WebGLCubeRenderTarget,
50
- Inspector: Inspector,
51
49
  R3F_BUILD_LEGACY: R3F_BUILD_LEGACY,
52
50
  R3F_BUILD_WEBGPU: R3F_BUILD_WEBGPU,
53
51
  RenderTarget: RenderTarget,
54
52
  RenderTargetCompat: WebGLRenderTarget,
55
- WebGPURenderer: WebGPURenderer
53
+ WebGPURenderer: WebGPURenderer,
54
+ loadInspector: loadInspector
56
55
  }, [three]);
57
56
 
58
57
  const primaryRegistry = /* @__PURE__ */ new Map();
@@ -515,8 +514,10 @@ function hasVisibilityHandlers(handlers) {
515
514
  }
516
515
 
517
516
  const FROM_REF = Symbol.for("@react-three/fiber.fromRef");
518
- function fromRef(ref) {
519
- return { [FROM_REF]: ref };
517
+ function fromRef(ref, transform) {
518
+ const marker = { [FROM_REF]: ref };
519
+ if (transform) marker.transform = transform;
520
+ return marker;
520
521
  }
521
522
  function isFromRef(value) {
522
523
  return value !== null && typeof value === "object" && FROM_REF in value;
@@ -617,11 +618,9 @@ function diffProps(instance, newProps) {
617
618
  for (const prop in instance.props) {
618
619
  if (RESERVED_PROPS.includes(prop) || newProps.hasOwnProperty(prop)) continue;
619
620
  const { root, key } = resolve(instance.object, prop);
620
- if (root.constructor && root.constructor.length === 0) {
621
+ if (typeof root?.constructor === "function") {
621
622
  const ctor = getMemoizedPrototype(root);
622
623
  if (!is.und(ctor)) changedProps[prop] = ctor[key];
623
- } else {
624
- changedProps[prop] = 0;
625
624
  }
626
625
  }
627
626
  return changedProps;
@@ -911,7 +910,7 @@ const createStore = (invalidate, advance) => {
911
910
  size: newSize,
912
911
  viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, newSize) }
913
912
  }));
914
- getScheduler().invalidate();
913
+ get().invalidate();
915
914
  }
916
915
  }
917
916
  return;
@@ -926,15 +925,16 @@ const createStore = (invalidate, advance) => {
926
925
  viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, size) },
927
926
  _sizeImperative: true
928
927
  }));
929
- getScheduler().invalidate();
928
+ get().invalidate();
930
929
  },
931
930
  setDpr: (dpr) => set((state2) => {
932
931
  const resolved = calculateDpr(dpr);
933
932
  return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
934
933
  }),
935
934
  setFrameloop: (frameloop = "always") => {
935
+ const rootId = get().internal.rootId;
936
+ if (rootId) getScheduler().setRootFrameloop(rootId, frameloop);
936
937
  set(() => ({ frameloop }));
937
- getScheduler().frameloop = frameloop;
938
938
  },
939
939
  setError: (error) => set(() => ({ error })),
940
940
  error: null,
@@ -1050,17 +1050,11 @@ const createStore = (invalidate, advance) => {
1050
1050
  oldSize = size;
1051
1051
  oldDpr = viewport.dpr;
1052
1052
  updateCamera(camera, size);
1053
- if (internal.isSecondary && canvasTarget) {
1054
- if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
1055
- canvasTarget.setSize(size.width, size.height, false);
1056
- } else {
1057
- if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
1058
- actualRenderer.setSize(size.width, size.height, false);
1059
- if (canvasTarget) {
1060
- if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
1061
- canvasTarget.setSize(size.width, size.height, false);
1062
- }
1063
- }
1053
+ const resizeTarget = canvasTarget ?? actualRenderer;
1054
+ if (viewport.dpr > 0) resizeTarget.setPixelRatio(viewport.dpr);
1055
+ resizeTarget.setSize(size.width, size.height, false);
1056
+ const activeTarget = actualRenderer.getCanvasTarget?.();
1057
+ if (!canvasTarget || activeTarget !== canvasTarget) internal.canvasTargetSizeDirty = true;
1064
1058
  }
1065
1059
  if (camera !== oldCamera) {
1066
1060
  oldCamera = camera;
@@ -1122,7 +1116,11 @@ function useLoader(loader, input, extensions, onProgress, cacheKey) {
1122
1116
  const results = cacheKeys.map(
1123
1117
  (key, index) => suspend(() => fn(loader, inputs[index]), [loader, key], { equal: is.equ })
1124
1118
  );
1125
- return Array.isArray(input) ? results : results[0];
1119
+ const stableRef = useRef(null);
1120
+ const previous = stableRef.current;
1121
+ const stable = previous !== null && previous.length === results.length && previous.every((value, i) => value === results[i]) ? previous : results;
1122
+ stableRef.current = stable;
1123
+ return Array.isArray(input) ? stable : stable[0];
1126
1124
  }
1127
1125
  useLoader.preload = function(loader, input, extensions, onProgress, cacheKey) {
1128
1126
  const inputs = Array.isArray(input) ? input : [input];
@@ -1242,6 +1240,13 @@ function useFrame(callback, priorityOrOptions) {
1242
1240
  * Use for controlling the entire frame loop, adding phases, etc.
1243
1241
  */
1244
1242
  scheduler: scheduler2,
1243
+ /**
1244
+ * The root that currently owns this job. Resolve on access because ambient
1245
+ * jobs can be adopted by a Canvas after the controls object is created.
1246
+ */
1247
+ get rootId() {
1248
+ return getScheduler().getJobRootId(id);
1249
+ },
1245
1250
  /**
1246
1251
  * Manually step this job only.
1247
1252
  * Bypasses FPS limiting - always runs.
@@ -1258,6 +1263,14 @@ function useFrame(callback, priorityOrOptions) {
1258
1263
  stepAll: (timestamp) => {
1259
1264
  getScheduler().step(timestamp);
1260
1265
  },
1266
+ /**
1267
+ * Request frames for this job's owning root without waking sibling roots.
1268
+ */
1269
+ invalidate: (frames, stackFrames) => {
1270
+ const scheduler3 = getScheduler();
1271
+ const rootId = scheduler3.getJobRootId(id);
1272
+ if (rootId) scheduler3.invalidateRoot(rootId, frames, stackFrames);
1273
+ },
1261
1274
  /**
1262
1275
  * Pause this job (set enabled=false).
1263
1276
  * Job remains registered but won't run.
@@ -1287,6 +1300,11 @@ function getUrls(input) {
1287
1300
  if (Array.isArray(input)) return input;
1288
1301
  return Object.values(input);
1289
1302
  }
1303
+ function inputSignature(input) {
1304
+ if (typeof input === "string") return input;
1305
+ if (Array.isArray(input)) return input.join("\0");
1306
+ return Object.keys(input).sort().map((key) => `${key}\0${input[key]}`).join("");
1307
+ }
1290
1308
  function allUrlsCached(urls, textureCache) {
1291
1309
  return urls.every((url) => textureCache.has(url));
1292
1310
  }
@@ -1312,24 +1330,37 @@ function useTexture(input, optionsOrOnLoad) {
1312
1330
  const onLoadRef = useRef(onLoad);
1313
1331
  onLoadRef.current = onLoad;
1314
1332
  const onLoadCalledForRef = useRef(null);
1315
- const urls = useMemo(() => getUrls(input), [input]);
1333
+ const inputKey = useMemo(() => inputSignature(input), [input]);
1334
+ const stableInput = useMemo(() => input, [inputKey]);
1335
+ const urls = useMemo(() => getUrls(stableInput), [stableInput]);
1316
1336
  const cachedResult = useMemo(() => {
1317
1337
  if (!cache) return null;
1318
1338
  const textures = store.getState().textures;
1319
1339
  if (!allUrlsCached(urls, textures)) return null;
1320
- return buildFromCache(input, textures);
1321
- }, [cache, urls, input, store]);
1340
+ return buildFromCache(stableInput, textures);
1341
+ }, [cache, urls, stableInput, store]);
1322
1342
  const loadedTextures = useLoader(
1323
1343
  TextureLoader,
1324
- IsObject(input) ? Object.values(input) : input
1344
+ IsObject(stableInput) ? Object.values(stableInput) : stableInput
1325
1345
  );
1326
- const inputKey = urls.join("\0");
1346
+ const mappedTextures = useMemo(() => {
1347
+ if (cachedResult) return cachedResult;
1348
+ if (IsObject(stableInput)) {
1349
+ const keyed = {};
1350
+ const textureArray = loadedTextures;
1351
+ let i = 0;
1352
+ for (const key in stableInput) keyed[key] = textureArray[i++];
1353
+ return keyed;
1354
+ } else {
1355
+ return loadedTextures;
1356
+ }
1357
+ }, [stableInput, loadedTextures, cachedResult]);
1327
1358
  useLayoutEffect(() => {
1328
1359
  if (cachedResult) return;
1329
1360
  if (onLoadCalledForRef.current === inputKey) return;
1330
1361
  onLoadCalledForRef.current = inputKey;
1331
- onLoadRef.current?.(loadedTextures);
1332
- }, [cachedResult, loadedTextures, inputKey]);
1362
+ onLoadRef.current?.(mappedTextures);
1363
+ }, [cachedResult, mappedTextures, inputKey]);
1333
1364
  useEffect(() => {
1334
1365
  if (cachedResult) return;
1335
1366
  if ("initTexture" in renderer) {
@@ -1348,30 +1379,18 @@ function useTexture(input, optionsOrOnLoad) {
1348
1379
  });
1349
1380
  }
1350
1381
  }, [renderer, loadedTextures, cachedResult]);
1351
- const mappedTextures = useMemo(() => {
1352
- if (cachedResult) return cachedResult;
1353
- if (IsObject(input)) {
1354
- const keyed = {};
1355
- const textureArray = loadedTextures;
1356
- let i = 0;
1357
- for (const key in input) keyed[key] = textureArray[i++];
1358
- return keyed;
1359
- } else {
1360
- return loadedTextures;
1361
- }
1362
- }, [input, loadedTextures, cachedResult]);
1363
1382
  useEffect(() => {
1364
1383
  if (!cache) return;
1365
1384
  const urlTextureMap = [];
1366
- if (typeof input === "string") {
1367
- urlTextureMap.push([input, mappedTextures]);
1368
- } else if (Array.isArray(input)) {
1385
+ if (typeof stableInput === "string") {
1386
+ urlTextureMap.push([stableInput, mappedTextures]);
1387
+ } else if (Array.isArray(stableInput)) {
1369
1388
  const textureArray = mappedTextures;
1370
- input.forEach((url, i) => urlTextureMap.push([url, textureArray[i]]));
1371
- } else if (IsObject(input)) {
1389
+ stableInput.forEach((url, i) => urlTextureMap.push([url, textureArray[i]]));
1390
+ } else if (IsObject(stableInput)) {
1372
1391
  const textureRecord = mappedTextures;
1373
- for (const key in input) {
1374
- const url = input[key];
1392
+ for (const key in stableInput) {
1393
+ const url = stableInput[key];
1375
1394
  urlTextureMap.push([url, textureRecord[key]]);
1376
1395
  }
1377
1396
  }
@@ -1400,7 +1419,7 @@ function useTexture(input, optionsOrOnLoad) {
1400
1419
  }
1401
1420
  return { _textureRefs: refs };
1402
1421
  });
1403
- }, [cache, input, mappedTextures, store]);
1422
+ }, [cache, stableInput, mappedTextures, store]);
1404
1423
  return mappedTextures;
1405
1424
  }
1406
1425
  useTexture.preload = (url) => useLoader.preload(TextureLoader, url);
@@ -1561,7 +1580,7 @@ function useEnvironment({
1561
1580
  texture = texture.renderTarget?.texture;
1562
1581
  }
1563
1582
  texture.mapping = isCubemap ? CubeReflectionMapping : EquirectangularReflectionMapping;
1564
- texture.colorSpace = colorSpace ?? (isCubemap ? "srgb" : "srgb-linear");
1583
+ texture.colorSpace = colorSpace ?? (isCubemap && extension !== "hdr-cube" ? "srgb" : "srgb-linear");
1565
1584
  return texture;
1566
1585
  }
1567
1586
  const preloadDefaultOptions = {
@@ -1614,11 +1633,12 @@ function getExtension(files) {
1614
1633
  const isCubemap = isArray(files) && files.length === 6;
1615
1634
  const isGainmap = isArray(files) && files.length === 3 && files.some((file) => file.endsWith("json"));
1616
1635
  const firstEntry = isArray(files) ? files[0] : files;
1617
- const extension = isCubemap ? "cube" : isGainmap ? "webp" : firstEntry.startsWith("data:application/exr") ? "exr" : firstEntry.startsWith("data:application/hdr") ? "hdr" : firstEntry.startsWith("data:image/jpeg") ? "jpg" : firstEntry.split(".").pop()?.split("?")?.shift()?.toLowerCase();
1636
+ const firstExtension = firstEntry.split(".").pop()?.split("?")?.shift()?.toLowerCase();
1637
+ const extension = isCubemap ? firstExtension === "hdr" ? "hdr-cube" : "cube" : isGainmap ? "webp" : firstEntry.startsWith("data:application/exr") ? "exr" : firstEntry.startsWith("data:application/hdr") ? "hdr" : firstEntry.startsWith("data:image/jpeg") ? "jpg" : firstExtension;
1618
1638
  return { extension, isCubemap, isGainmap };
1619
1639
  }
1620
1640
  function getLoader(extension) {
1621
- const loader = extension === "cube" ? CubeTextureLoader : extension === "hdr" ? HDRLoader : extension === "exr" ? EXRLoader : extension === "jpg" || extension === "jpeg" ? UltraHDRLoader : extension === "webp" ? GainMapLoader : null;
1641
+ const loader = extension === "cube" ? CubeTextureLoader : extension === "hdr-cube" ? HDRCubeTextureLoader : extension === "hdr" ? HDRLoader : extension === "exr" ? EXRLoader : extension === "jpg" || extension === "jpeg" ? UltraHDRLoader : extension === "webp" ? GainMapLoader : null;
1622
1642
  return loader;
1623
1643
  }
1624
1644
 
@@ -1669,13 +1689,19 @@ function addTail(callback) {
1669
1689
  return getScheduler().onIdle(callback);
1670
1690
  }
1671
1691
  function invalidate(state, frames = 1, stackFrames = false) {
1672
- getScheduler().invalidate(frames, stackFrames);
1692
+ const rootId = state?.internal?.rootId;
1693
+ const scheduler = getScheduler();
1694
+ if (rootId) scheduler.invalidateRoot(rootId, frames, stackFrames);
1695
+ else scheduler.invalidate(frames, stackFrames);
1673
1696
  }
1674
- function advance(timestamp) {
1675
- getScheduler().step(timestamp);
1697
+ function advance(timestamp, runGlobalEffects, state) {
1698
+ const rootId = state?.internal?.rootId;
1699
+ const scheduler = getScheduler();
1700
+ if (rootId) scheduler.stepRoot(rootId, timestamp);
1701
+ else scheduler.step(timestamp);
1676
1702
  }
1677
1703
 
1678
- const version = "10.0.0-alpha.3";
1704
+ const version = "10.0.0-alpha.5";
1679
1705
  const packageData = {
1680
1706
  version: version};
1681
1707
 
@@ -13534,7 +13560,10 @@ function extend(objects) {
13534
13560
  catalogue[Component] = objects;
13535
13561
  return Component;
13536
13562
  } else {
13537
- Object.assign(catalogue, objects);
13563
+ for (const name in objects) {
13564
+ const object = objects[name];
13565
+ if (isConstructor(object)) catalogue[name] = object;
13566
+ }
13538
13567
  }
13539
13568
  }
13540
13569
  function validateInstance(type, props) {
@@ -13882,7 +13911,7 @@ const reconciler = /* @__PURE__ */ createReconciler({
13882
13911
  getRootHostContext: () => NO_CONTEXT,
13883
13912
  getChildHostContext: () => NO_CONTEXT,
13884
13913
  commitUpdate(instance, type, oldProps, newProps, fiber) {
13885
- validateInstance(type, newProps);
13914
+ validateInstance(instance.type, newProps);
13886
13915
  let reconstruct = false;
13887
13916
  if (instance.type === "primitive" && oldProps.object !== newProps.object) reconstruct = true;
13888
13917
  else if (newProps.args?.length !== oldProps.args?.length) reconstruct = true;
@@ -13909,8 +13938,8 @@ const reconciler = /* @__PURE__ */ createReconciler({
13909
13938
  for (const prop in instance.props) {
13910
13939
  const value = instance.props[prop];
13911
13940
  if (isFromRef(value)) {
13912
- const ref = value[FROM_REF];
13913
- if (ref.current != null) resolved[prop] = ref.current;
13941
+ const { [FROM_REF]: ref, transform } = value;
13942
+ if (ref.current != null) resolved[prop] = transform ? transform(ref.current) : ref.current;
13914
13943
  }
13915
13944
  }
13916
13945
  if (Object.keys(resolved).length) applyProps(instance.object, resolved);
@@ -14287,7 +14316,7 @@ function createRoot(canvas) {
14287
14316
  const handleXRFrame = (timestamp, _frame) => {
14288
14317
  const state2 = store.getState();
14289
14318
  if (state2.frameloop === "never") return;
14290
- advance(timestamp);
14319
+ advance(timestamp, true, state2);
14291
14320
  };
14292
14321
  const actualRenderer = state.internal.actualRenderer;
14293
14322
  const handleSessionChange = () => {
@@ -14295,7 +14324,7 @@ function createRoot(canvas) {
14295
14324
  const renderer2 = state2.internal.actualRenderer;
14296
14325
  actualRenderer.xr.enabled = actualRenderer.xr.isPresenting;
14297
14326
  renderer2.xr.setAnimationLoop(renderer2.xr.isPresenting ? handleXRFrame : null);
14298
- if (!renderer2.xr.isPresenting) invalidate();
14327
+ if (!renderer2.xr.isPresenting) invalidate(state2);
14299
14328
  };
14300
14329
  const xr = {
14301
14330
  connect() {
@@ -14377,14 +14406,23 @@ function createRoot(canvas) {
14377
14406
  const newRootId = canvasId || scheduler.generateRootId();
14378
14407
  const unregisterRoot = scheduler.registerRoot(newRootId, {
14379
14408
  getState: () => store.getState(),
14380
- onError: (err) => store.getState().setError(err)
14409
+ onError: (err) => store.getState().setError(err),
14410
+ frameloop: store.getState().frameloop,
14411
+ before: schedulerConfig?.before,
14412
+ after: schedulerConfig?.after,
14413
+ order: schedulerConfig?.order
14381
14414
  });
14382
14415
  const unregisterCanvasTarget = scheduler.register(
14383
14416
  () => {
14384
14417
  const state2 = store.getState();
14385
- if (state2.internal.isMultiCanvas && state2.internal.canvasTarget) {
14418
+ const canvasTarget = state2.internal.canvasTarget;
14419
+ if (canvasTarget) {
14386
14420
  const renderer2 = state2.internal.actualRenderer;
14387
- renderer2.setCanvasTarget(state2.internal.canvasTarget);
14421
+ if (renderer2.getCanvasTarget() !== canvasTarget) renderer2.setCanvasTarget(canvasTarget);
14422
+ if (state2.internal.canvasTargetSizeDirty) {
14423
+ state2.internal.canvasTargetSizeDirty = false;
14424
+ renderer2.backend?.updateSize?.();
14425
+ }
14388
14426
  }
14389
14427
  },
14390
14428
  {
@@ -14453,8 +14491,7 @@ function createRoot(canvas) {
14453
14491
  phase: "render",
14454
14492
  system: true,
14455
14493
  // Internal flag: this is a system job, not user-controlled
14456
- // Apply scheduler config for render ordering and rate limiting
14457
- ...schedulerConfig?.after && { after: schedulerConfig.after },
14494
+ // FPS throttles only the default render job; Canvas ordering belongs to the root.
14458
14495
  ...schedulerConfig?.fps && { fps: schedulerConfig.fps }
14459
14496
  }
14460
14497
  );
@@ -14473,8 +14510,21 @@ function createRoot(canvas) {
14473
14510
  scheduler
14474
14511
  }
14475
14512
  }));
14513
+ } else {
14514
+ const constraintsChanged = !is.equ(schedulerConfig?.before, lastConfiguredProps.schedulerBefore, shallowLoose) || !is.equ(schedulerConfig?.after, lastConfiguredProps.schedulerAfter, shallowLoose);
14515
+ if (constraintsChanged) {
14516
+ scheduler.setRootConstraints(rootId, {
14517
+ before: schedulerConfig?.before,
14518
+ after: schedulerConfig?.after
14519
+ });
14520
+ }
14521
+ if (schedulerConfig?.order !== lastConfiguredProps.schedulerOrder) {
14522
+ scheduler.setRootOrder(rootId, schedulerConfig?.order ?? 0);
14523
+ }
14476
14524
  }
14477
- scheduler.frameloop = frameloop;
14525
+ lastConfiguredProps.schedulerBefore = schedulerConfig?.before;
14526
+ lastConfiguredProps.schedulerAfter = schedulerConfig?.after;
14527
+ lastConfiguredProps.schedulerOrder = schedulerConfig?.order;
14478
14528
  onCreated = onCreatedCallback;
14479
14529
  configured = true;
14480
14530
  resolve();
@@ -14516,18 +14566,23 @@ function unmountComponentAtNode(canvas, callback) {
14516
14566
  const fiber = root?.fiber;
14517
14567
  if (fiber) {
14518
14568
  const state = root?.store.getState();
14519
- if (state) state.internal.active = false;
14569
+ if (state) {
14570
+ state.internal.active = false;
14571
+ const unregisterRoot = state.internal.unregisterRoot;
14572
+ if (unregisterRoot) {
14573
+ unregisterRoot();
14574
+ state.internal.unregisterRoot = void 0;
14575
+ }
14576
+ }
14520
14577
  reconciler.updateContainer(null, fiber, null, () => {
14521
14578
  if (state) {
14522
14579
  setTimeout(() => {
14523
14580
  try {
14524
14581
  const renderer = state.internal.actualRenderer;
14525
- const unregisterRoot = state.internal.unregisterRoot;
14526
- if (unregisterRoot) unregisterRoot();
14527
14582
  const unregisterPrimary = state.internal.unregisterPrimary;
14528
14583
  if (unregisterPrimary) unregisterPrimary();
14529
14584
  const canvasTarget = state.internal.canvasTarget;
14530
- if (canvasTarget?.dispose) canvasTarget.dispose();
14585
+ if (state.internal.isSecondary && canvasTarget?.dispose) canvasTarget.dispose();
14531
14586
  state.events.disconnect?.();
14532
14587
  cleanupHelperGroup(root.store);
14533
14588
  if (state.isLegacy && renderer) {
@@ -14668,16 +14723,11 @@ function setEnvProps(background, scene, defaultScene, texture, sceneProps = {})
14668
14723
  const oldbg = target.background;
14669
14724
  const oldenv = target.environment;
14670
14725
  const oldSceneProps = {
14671
- // @ts-ignore
14672
14726
  backgroundBlurriness: target.backgroundBlurriness,
14673
- // @ts-ignore
14674
14727
  backgroundIntensity: target.backgroundIntensity,
14675
- // @ts-ignore
14676
- backgroundRotation: target.backgroundRotation?.clone?.() ?? [0, 0, 0],
14677
- // @ts-ignore
14728
+ backgroundRotation: target.backgroundRotation.clone(),
14678
14729
  environmentIntensity: target.environmentIntensity,
14679
- // @ts-ignore
14680
- environmentRotation: target.environmentRotation?.clone?.() ?? [0, 0, 0]
14730
+ environmentRotation: target.environmentRotation.clone()
14681
14731
  };
14682
14732
  if (background !== "only") target.environment = texture;
14683
14733
  if (background) target.background = texture;
@@ -14971,9 +15021,9 @@ function CanvasImpl({
14971
15021
  React.useMemo(() => extend(THREE), []);
14972
15022
  const Bridge = useBridge();
14973
15023
  const backgroundProps = React.useMemo(() => parseBackground(background), [background]);
14974
- const hasInitialSizeRef = React.useRef(false);
15024
+ const [hasInitialSize, setHasInitialSize] = React.useState(false);
14975
15025
  const measureConfig = React.useMemo(() => {
14976
- if (!hasInitialSizeRef.current) {
15026
+ if (!hasInitialSize) {
14977
15027
  return {
14978
15028
  ...resize,
14979
15029
  scroll: resize?.scroll ?? true,
@@ -14985,7 +15035,7 @@ function CanvasImpl({
14985
15035
  debounce: { scroll: 50, resize: 0 },
14986
15036
  ...resize
14987
15037
  };
14988
- }, [resize, hasInitialSizeRef.current]);
15038
+ }, [hasInitialSize, resize]);
14989
15039
  const [containerRef, containerRect] = useMeasure(measureConfig);
14990
15040
  const effectiveSize = React.useMemo(() => {
14991
15041
  let w = width ?? containerRect.width;
@@ -15001,9 +15051,7 @@ function CanvasImpl({
15001
15051
  left: containerRect.left
15002
15052
  };
15003
15053
  }, [width, height, containerRect, forceEven]);
15004
- if (!hasInitialSizeRef.current && effectiveSize.width > 0 && effectiveSize.height > 0) {
15005
- hasInitialSizeRef.current = true;
15006
- }
15054
+ if (!hasInitialSize && effectiveSize.width > 0 && effectiveSize.height > 0) setHasInitialSize(true);
15007
15055
  const canvasRef = React.useRef(null);
15008
15056
  const divRef = React.useRef(null);
15009
15057
  React.useImperativeHandle(ref, () => canvasRef.current);
@@ -15117,6 +15165,7 @@ function CanvasImpl({
15117
15165
  const canvas = canvasRef.current;
15118
15166
  if (canvas) {
15119
15167
  return () => {
15168
+ if (canvas.isConnected) return;
15120
15169
  unmountComponentAtNode(canvas);
15121
15170
  root.current = null;
15122
15171
  };