@react-three/fiber 10.0.0-canary.f50076a → 10.0.0-canary.f511bb4

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.
Files changed (32) hide show
  1. package/dist/extension.d.cts +3 -3
  2. package/dist/extension.d.mts +3 -3
  3. package/dist/extension.d.ts +3 -3
  4. package/dist/extension.mjs +1 -1
  5. package/dist/index.cjs +1 -1
  6. package/dist/index.d.cts +5 -5
  7. package/dist/index.d.mts +5 -5
  8. package/dist/index.d.ts +5 -5
  9. package/dist/index.mjs +3 -3
  10. package/dist/legacy.cjs +1 -1
  11. package/dist/legacy.d.cts +4 -4
  12. package/dist/legacy.d.mts +4 -4
  13. package/dist/legacy.d.ts +4 -4
  14. package/dist/legacy.mjs +4 -4
  15. package/dist/shared/{fiber.DAl4H27x.d.cts → fiber.-1JhXWEC.d.cts} +1 -1
  16. package/dist/shared/{fiber.dvUbwHHn.mjs → fiber.B3mc9NA1.mjs} +306 -87
  17. package/dist/shared/{fiber.DNehrxK5.d.ts → fiber.Bkpap4yF.d.mts} +76 -1
  18. package/dist/shared/{fiber.TWXdbcWP.d.mts → fiber.BnIfHyTk.d.cts} +76 -1
  19. package/dist/shared/{fiber.I5Q4dVvl.d.cts → fiber.CLTHZhZp.d.cts} +30 -2
  20. package/dist/shared/{fiber.I5Q4dVvl.d.mts → fiber.CLTHZhZp.d.mts} +30 -2
  21. package/dist/shared/{fiber.I5Q4dVvl.d.ts → fiber.CLTHZhZp.d.ts} +30 -2
  22. package/dist/shared/{fiber.Cx-UAmMb.d.ts → fiber.CTEAJNmg.d.ts} +1 -1
  23. package/dist/shared/{fiber.B0oukOFO.mjs → fiber.CUKAa7Rv.mjs} +1 -1
  24. package/dist/shared/{fiber.BU0vBHc9.d.mts → fiber.CksZuiOV.d.mts} +1 -1
  25. package/dist/shared/{fiber.CEjezJYZ.cjs → fiber.DF7oitV8.cjs} +306 -87
  26. package/dist/shared/{fiber.11sXr8Eb.d.cts → fiber.NwhGYEh-.d.ts} +76 -1
  27. package/dist/webgpu/index.cjs +1 -1
  28. package/dist/webgpu/index.d.cts +4 -4
  29. package/dist/webgpu/index.d.mts +4 -4
  30. package/dist/webgpu/index.d.ts +4 -4
  31. package/dist/webgpu/index.mjs +4 -4
  32. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
2
- import { n as notifyDepreciated, a as useThree, u as useStore, h as unstageTextures, i as stageTextures, j as detachRootExtensions, k as attachRootExtensions, d as useIsomorphicLayoutEffect, e as useMutableCallback, c as context, b as useFrame, f as useBridge, l as notifyAlpha, E as ErrorBoundary, B as Block, m as notifyRootExtensionsHmr } from './fiber.B0oukOFO.mjs';
2
+ import { n as notifyDepreciated, a as useThree, u as useStore, h as unstageTextures, i as stageTextures, j as attachRootExtensions, k as detachRootExtensions, d as useIsomorphicLayoutEffect, e as useMutableCallback, c as context, b as useFrame, f as useBridge, l as notifyAlpha, E as ErrorBoundary, B as Block, m as notifyRootExtensionsHmr } from './fiber.CUKAa7Rv.mjs';
3
3
  import * as React from 'react';
4
4
  import React__default, { useRef, useMemo, useLayoutEffect, useEffect, useImperativeHandle, useCallback, useState } from 'react';
5
5
  import useMeasure from 'react-use-measure';
@@ -422,6 +422,28 @@ const VISIBILITY_EVENT_REGEX = /^on(Framed|Occluded|Visible)$/;
422
422
  const INDEX_REGEX = /-\d+$/;
423
423
  const MEMOIZED_PROTOTYPES = /* @__PURE__ */ new Map();
424
424
  const colorMaps = ["map", "emissiveMap", "sheenColorMap", "specularColorMap", "envMap"];
425
+ const programProps = /* @__PURE__ */ new Set([
426
+ "transparent",
427
+ "vertexColors",
428
+ "flatShading",
429
+ "fog",
430
+ // Culling is live, but DOUBLE_SIDED / FLIP_SIDED normal flipping is compiled in
431
+ "side",
432
+ "alphaHash",
433
+ "premultipliedAlpha",
434
+ // Both feed the OPAQUE define when the material is not transparent
435
+ "blending",
436
+ "alphaToCoverage",
437
+ "dithering",
438
+ "sizeAttenuation",
439
+ "combine",
440
+ "normalMapType",
441
+ "depthPacking"
442
+ ]);
443
+ function changesProgram(root, key, previous, next) {
444
+ if (!root?.isMaterial || previous === next) return false;
445
+ return programProps.has(key) || isTexture(previous) !== isTexture(next);
446
+ }
425
447
  function resolve(root, key) {
426
448
  if (!key.includes("-")) return { root, key, target: root[key] };
427
449
  if (key in root) return { root, key, target: root[key] };
@@ -452,6 +474,7 @@ function attach(parent, child) {
452
474
  const { root, key } = resolve(parent.object, child.props.attach);
453
475
  child.previousAttach = root[key];
454
476
  root[key] = child.object;
477
+ if (changesProgram(root, key, child.previousAttach, child.object)) root.needsUpdate = true;
455
478
  } else if (is.fun(child.props.attach)) {
456
479
  child.previousAttach = child.props.attach(parent.object, child.object);
457
480
  }
@@ -462,6 +485,7 @@ function detach(parent, child) {
462
485
  const previous = child.previousAttach;
463
486
  if (previous === void 0) delete root[key];
464
487
  else root[key] = previous;
488
+ if (changesProgram(root, key, child.object, previous)) root.needsUpdate = true;
465
489
  } else {
466
490
  child.previousAttach?.(parent.object, child.object);
467
491
  }
@@ -576,9 +600,12 @@ function applyProps(object, props) {
576
600
  }
577
601
  } else {
578
602
  if (key === "material") assertNodeMaterialSupported(rootState, value);
603
+ const previous = root[key];
579
604
  root[key] = value;
580
605
  if (key.endsWith("Node") && root.isMaterial) {
581
606
  root.needsUpdate = true;
607
+ } else if (changesProgram(root, key, previous, value)) {
608
+ root.needsUpdate = true;
582
609
  }
583
610
  const three = rootState && getThree();
584
611
  if (three && rootState.renderer?.outputColorSpace === three.SRGBColorSpace && colorMaps.includes(key) && isTexture(value) && root[key]?.isTexture && // sRGB textures must be RGBA8 since r137 https://github.com/mrdoob/three.js/pull/23129
@@ -14769,6 +14796,69 @@ function getPrimaryIds() {
14769
14796
  return Array.from(primaryRegistry.keys());
14770
14797
  }
14771
14798
 
14799
+ const fulfilled = (value) => Object.assign(Promise.resolve(value), { status: "fulfilled", value });
14800
+ const tracked = (promise) => {
14801
+ const result = Promise.resolve(promise);
14802
+ result.status = "pending";
14803
+ result.then(
14804
+ (value) => {
14805
+ Object.assign(result, { status: "fulfilled", value });
14806
+ },
14807
+ (reason) => {
14808
+ Object.assign(result, { status: "rejected", reason });
14809
+ }
14810
+ );
14811
+ return result;
14812
+ };
14813
+
14814
+ const records = /* @__PURE__ */ new WeakMap();
14815
+ function take(renderer, record) {
14816
+ record.leases++;
14817
+ let released = false;
14818
+ return () => {
14819
+ if (released) return;
14820
+ released = true;
14821
+ if (--record.leases > 0) return;
14822
+ records.delete(renderer);
14823
+ if (record.owned) return disposeRenderer(renderer);
14824
+ };
14825
+ }
14826
+ function leaseRenderer(renderer, owned) {
14827
+ let record = records.get(renderer);
14828
+ if (!record) records.set(renderer, record = { owned, leases: 0 });
14829
+ return take(renderer, record);
14830
+ }
14831
+ function borrowRenderer(renderer) {
14832
+ const record = records.get(renderer);
14833
+ if (!record) throw new Error("R3F: cannot share a renderer whose canvas has already unmounted");
14834
+ return take(renderer, record);
14835
+ }
14836
+ function disposeRenderer(renderer) {
14837
+ if (renderer.hasInitialized?.() === false) return;
14838
+ const warn = (error) => console.warn("[R3F] Error disposing renderer", error);
14839
+ const loseContext = () => {
14840
+ try {
14841
+ ;
14842
+ renderer.forceContextLoss?.();
14843
+ } catch (error) {
14844
+ warn(error);
14845
+ }
14846
+ };
14847
+ let disposed;
14848
+ try {
14849
+ disposed = renderer.dispose();
14850
+ } catch (error) {
14851
+ warn(error);
14852
+ }
14853
+ if (typeof disposed?.then === "function") {
14854
+ return Promise.resolve(disposed).then(loseContext, (error) => {
14855
+ warn(error);
14856
+ loseContext();
14857
+ });
14858
+ }
14859
+ loseContext();
14860
+ }
14861
+
14772
14862
  const isRenderer = (def) => !!def?.render;
14773
14863
  const _roots = /* @__PURE__ */ new Map();
14774
14864
  function ensureStoreThreeObjects(store) {
@@ -14780,9 +14870,9 @@ function ensureStoreThreeObjects(store) {
14780
14870
  }
14781
14871
  const shallowLoose = { objects: "shallow", strict: false };
14782
14872
  async function resolveRenderer(config, defaultProps, RendererClass) {
14783
- if (typeof config === "function") return await config(defaultProps);
14784
- if (isRenderer(config)) return config;
14785
- return new RendererClass({ ...defaultProps, ...config });
14873
+ if (typeof config === "function") return { renderer: await config(defaultProps), owned: true };
14874
+ if (isRenderer(config)) return { renderer: config, owned: false };
14875
+ return { renderer: new RendererClass({ ...defaultProps, ...config }), owned: true };
14786
14876
  }
14787
14877
  function computeInitialSize(canvas, size) {
14788
14878
  if (!size && typeof HTMLCanvasElement !== "undefined" && canvas instanceof HTMLCanvasElement && canvas.parentElement) {
@@ -14798,6 +14888,47 @@ function computeInitialSize(canvas, size) {
14798
14888
  }
14799
14889
  return { width: 0, height: 0, top: 0, left: 0, ...size };
14800
14890
  }
14891
+ function watchDpr(onChange) {
14892
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function" || !window.devicePixelRatio) {
14893
+ return () => {
14894
+ };
14895
+ }
14896
+ let query;
14897
+ const listen = () => {
14898
+ try {
14899
+ query = window.matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`);
14900
+ if (typeof query?.addEventListener === "function") query.addEventListener("change", handleChange);
14901
+ else query?.addListener?.(handleChange);
14902
+ } catch {
14903
+ query = void 0;
14904
+ }
14905
+ };
14906
+ const unlisten = () => {
14907
+ if (typeof query?.removeEventListener === "function") query.removeEventListener("change", handleChange);
14908
+ else query?.removeListener?.(handleChange);
14909
+ query = void 0;
14910
+ };
14911
+ const handleChange = () => {
14912
+ unlisten();
14913
+ listen();
14914
+ onChange();
14915
+ };
14916
+ listen();
14917
+ return unlisten;
14918
+ }
14919
+ async function createOwnedWebGPURenderer(rendererConfig, defaultGPUProps, canvas, propsSize, dpr, support) {
14920
+ const resolved = await resolveRenderer(rendererConfig, defaultGPUProps, support.Renderer);
14921
+ const renderer = resolved.renderer;
14922
+ if (!renderer.hasInitialized?.()) {
14923
+ const size = computeInitialSize(canvas, propsSize);
14924
+ if (size.width > 0 && size.height > 0) {
14925
+ renderer.setPixelRatio(calculateDpr(dpr));
14926
+ renderer.setSize(size.width, size.height, false);
14927
+ }
14928
+ await renderer.init();
14929
+ }
14930
+ return { renderer, owned: resolved.owned };
14931
+ }
14801
14932
  function createRoot(canvas, provider) {
14802
14933
  const prevRoot = _roots.get(canvas);
14803
14934
  const prevFiber = prevRoot?.fiber;
@@ -14834,15 +14965,39 @@ function createRoot(canvas, provider) {
14834
14965
  null
14835
14966
  // transitionCallbacks
14836
14967
  );
14837
- if (!prevRoot) _roots.set(canvas, { fiber, store });
14968
+ const root = prevRoot || { fiber, store, unmountClaim: null, ready: fulfilled(void 0) };
14969
+ if (!prevRoot) _roots.set(canvas, root);
14838
14970
  let onCreated;
14839
14971
  let lastCamera;
14840
14972
  const lastConfiguredProps = {};
14973
+ let resolvedDpr = 0;
14974
+ let dprChangedInXR = false;
14975
+ let unwatchDpr;
14976
+ const resolveDpr = () => {
14977
+ if (_roots.get(canvas)?.store !== store) return unwatchDpr?.();
14978
+ const state = store.getState();
14979
+ const xr = state.internal.actualRenderer?.xr;
14980
+ if (xr?.isPresenting || xr?.getSession?.()) {
14981
+ dprChangedInXR = true;
14982
+ return;
14983
+ }
14984
+ dprChangedInXR = false;
14985
+ const dpr = lastConfiguredProps.dpr;
14986
+ if (dpr === void 0 || state.viewport.dpr !== resolvedDpr || calculateDpr(dpr) === resolvedDpr) return;
14987
+ state.setDpr(dpr);
14988
+ resolvedDpr = store.getState().viewport.dpr;
14989
+ };
14841
14990
  let configured = false;
14842
14991
  let pending = null;
14843
- let rendererSetup = null;
14992
+ const cancelUnmount = () => {
14993
+ if (!root.unmountClaim) return;
14994
+ root.unmountClaim = null;
14995
+ store.getState().internal.active = true;
14996
+ };
14844
14997
  return {
14845
14998
  async configure(props = {}) {
14999
+ if (_roots.get(canvas) !== root) throw new Error("R3F: cannot configure a root after it has unmounted");
15000
+ cancelUnmount();
14846
15001
  let resolve;
14847
15002
  pending = new Promise((_resolve) => resolve = _resolve);
14848
15003
  const {
@@ -14916,49 +15071,82 @@ function createRoot(canvas, provider) {
14916
15071
  );
14917
15072
  }
14918
15073
  if (!state.internal.actualRenderer) {
14919
- if (!rendererSetup) {
14920
- rendererSetup = (async () => {
15074
+ if (root.ready.status !== "pending") {
15075
+ const setup = (async () => {
14921
15076
  const support = wantsGL ? await webgl() : await webgpu();
14922
15077
  registerThree(support.three);
14923
15078
  state.internal.support = support;
14924
15079
  ensureStoreThreeObjects(store);
14925
15080
  if (support.kind === "webgl") {
14926
- renderer = await resolveRenderer(glConfig, defaultGLProps, support.Renderer);
15081
+ const resolved = await resolveRenderer(glConfig, defaultGLProps, support.Renderer);
15082
+ renderer = resolved.renderer;
14927
15083
  state.internal.actualRenderer = renderer;
15084
+ state.internal.releaseRenderer = leaseRenderer(renderer, resolved.owned);
14928
15085
  state.set({ isLegacy: true, gl: renderer, renderer, primaryStore: store });
14929
15086
  } else if (primaryCanvas) {
14930
15087
  const primary = await waitForPrimary(primaryCanvas);
14931
- renderer = primary.renderer;
14932
- state.internal.actualRenderer = renderer;
14933
- const canvasTarget = new support.CanvasTarget(canvas);
14934
- primary.store.setState((prev) => ({
14935
- internal: { ...prev.internal, isMultiCanvas: true }
14936
- }));
14937
- state.set((prev) => ({
14938
- webGPUSupported: primary.store.getState().webGPUSupported,
14939
- renderer,
14940
- primaryStore: primary.store,
14941
- internal: {
14942
- ...prev.internal,
14943
- canvasTarget,
14944
- isMultiCanvas: true,
14945
- isSecondary: true,
14946
- targetId: primaryCanvas
14947
- }
14948
- }));
14949
- } else {
14950
- renderer = await resolveRenderer(rendererConfig, defaultGPUProps, support.Renderer);
14951
- if (!renderer.hasInitialized?.()) {
14952
- const size2 = computeInitialSize(canvas, propsSize);
14953
- if (size2.width > 0 && size2.height > 0) {
14954
- renderer.setPixelRatio(calculateDpr(dpr));
14955
- renderer.setSize(size2.width, size2.height, false);
14956
- }
14957
- await renderer.init();
15088
+ if (primary.store.getState().webGPUSupported) {
15089
+ renderer = primary.renderer;
15090
+ state.internal.actualRenderer = renderer;
15091
+ state.internal.releaseRenderer = borrowRenderer(renderer);
15092
+ const canvasTarget = new support.CanvasTarget(canvas);
15093
+ primary.store.setState((prev) => ({
15094
+ internal: { ...prev.internal, isMultiCanvas: true }
15095
+ }));
15096
+ state.set((prev) => ({
15097
+ webGPUSupported: primary.store.getState().webGPUSupported,
15098
+ renderer,
15099
+ primaryStore: primary.store,
15100
+ internal: {
15101
+ ...prev.internal,
15102
+ canvasTarget,
15103
+ isMultiCanvas: true,
15104
+ isSecondary: true,
15105
+ targetId: primaryCanvas
15106
+ }
15107
+ }));
15108
+ } else {
15109
+ const isPlainConfig = is.obj(rendererConfig) && !is.fun(rendererConfig) && !isRenderer(rendererConfig);
15110
+ const { primaryCanvas: _peeled, ...rendererOptions } = isPlainConfig ? rendererConfig : {};
15111
+ const fallbackConfig = is.fun(rendererConfig) || isRenderer(rendererConfig) ? rendererConfig : { ...rendererOptions, forceWebGL: true };
15112
+ const resolved = await createOwnedWebGPURenderer(
15113
+ fallbackConfig,
15114
+ defaultGPUProps,
15115
+ canvas,
15116
+ propsSize,
15117
+ dpr,
15118
+ support
15119
+ );
15120
+ renderer = resolved.renderer;
15121
+ const backend = renderer.backend;
15122
+ const isWebGPUBackend = backend && "isWebGPUBackend" in backend;
15123
+ state.internal.actualRenderer = renderer;
15124
+ state.internal.releaseRenderer = leaseRenderer(renderer, resolved.owned);
15125
+ state.set((prev) => ({
15126
+ webGPUSupported: isWebGPUBackend,
15127
+ renderer,
15128
+ primaryStore: store,
15129
+ internal: {
15130
+ ...prev.internal,
15131
+ canvasTarget: renderer.getCanvasTarget?.(),
15132
+ sharedRendererFallback: true
15133
+ }
15134
+ }));
14958
15135
  }
15136
+ } else {
15137
+ const resolved = await createOwnedWebGPURenderer(
15138
+ rendererConfig,
15139
+ defaultGPUProps,
15140
+ canvas,
15141
+ propsSize,
15142
+ dpr,
15143
+ support
15144
+ );
15145
+ renderer = resolved.renderer;
14959
15146
  const backend = renderer.backend;
14960
15147
  const isWebGPUBackend = backend && "isWebGPUBackend" in backend;
14961
15148
  state.internal.actualRenderer = renderer;
15149
+ state.internal.releaseRenderer = leaseRenderer(renderer, resolved.owned);
14962
15150
  state.set({ webGPUSupported: isWebGPUBackend, renderer, primaryStore: store });
14963
15151
  if (canvasId && !state.internal.isSecondary) {
14964
15152
  const canvasTarget = renderer.getCanvasTarget?.();
@@ -14972,16 +15160,15 @@ function createRoot(canvas, provider) {
14972
15160
  }));
14973
15161
  }
14974
15162
  }
14975
- })().catch((err) => {
14976
- rendererSetup = null;
14977
- throw err;
14978
- });
15163
+ })();
15164
+ root.ready = tracked(setup);
14979
15165
  }
14980
- await rendererSetup;
15166
+ await root.ready;
14981
15167
  state = store.getState();
14982
15168
  renderer = state.internal.actualRenderer;
14983
15169
  }
14984
15170
  const three = getThree();
15171
+ const borrowsRenderer = state.internal.isSecondary === true;
14985
15172
  let raycaster = state.raycaster;
14986
15173
  if (!raycaster) state.set({ raycaster: raycaster = new three.Raycaster() });
14987
15174
  const { params, ...options } = raycastOptions || {};
@@ -15056,6 +15243,12 @@ function createRoot(canvas, provider) {
15056
15243
  if (dpr !== void 0 && !is.equ(dpr, lastConfiguredProps.dpr, shallowLoose)) {
15057
15244
  state.setDpr(dpr);
15058
15245
  lastConfiguredProps.dpr = dpr;
15246
+ resolvedDpr = store.getState().viewport.dpr;
15247
+ }
15248
+ const internal = store.getState().internal;
15249
+ if (Array.isArray(dpr) && (!unwatchDpr || internal.unwatchDpr !== unwatchDpr)) {
15250
+ internal.unwatchDpr?.();
15251
+ internal.unwatchDpr = unwatchDpr = watchDpr(resolveDpr);
15059
15252
  }
15060
15253
  if (frameloop !== void 0 && frameloop !== lastConfiguredProps.frameloop) {
15061
15254
  state.setFrameloop(frameloop);
@@ -15084,7 +15277,10 @@ function createRoot(canvas, provider) {
15084
15277
  const renderer2 = state2.internal.actualRenderer;
15085
15278
  actualRenderer.xr.enabled = actualRenderer.xr.isPresenting;
15086
15279
  renderer2.xr.setAnimationLoop(renderer2.xr.isPresenting ? handleXRFrame : null);
15087
- if (!renderer2.xr.isPresenting) invalidate(state2);
15280
+ if (!renderer2.xr.isPresenting) {
15281
+ if (dprChangedInXR) resolveDpr();
15282
+ invalidate(state2);
15283
+ }
15088
15284
  };
15089
15285
  const xr = {
15090
15286
  connect() {
@@ -15100,10 +15296,10 @@ function createRoot(canvas, provider) {
15100
15296
  xrManager.removeEventListener("sessionend", handleSessionChange);
15101
15297
  }
15102
15298
  };
15103
- if (typeof renderer.xr?.addEventListener === "function") xr.connect();
15299
+ if (!borrowsRenderer && typeof renderer.xr?.addEventListener === "function") xr.connect();
15104
15300
  state.set({ xr });
15105
15301
  }
15106
- if (renderer.shadowMap && !is.equ(shadows, lastConfiguredProps.shadows, shallowLoose)) {
15302
+ if (!borrowsRenderer && renderer.shadowMap && !is.equ(shadows, lastConfiguredProps.shadows, shallowLoose)) {
15107
15303
  lastConfiguredProps.shadows = shadows;
15108
15304
  const oldEnabled = renderer.shadowMap.enabled;
15109
15305
  const oldType = renderer.shadowMap.type;
@@ -15132,7 +15328,7 @@ function createRoot(canvas, provider) {
15132
15328
  renderer.shadowMap.needsUpdate = true;
15133
15329
  }
15134
15330
  }
15135
- if (!configured) {
15331
+ if (!configured && !borrowsRenderer) {
15136
15332
  renderer.outputColorSpace = three.SRGBColorSpace;
15137
15333
  renderer.toneMapping = three.ACESFilmicToneMapping;
15138
15334
  }
@@ -15141,7 +15337,7 @@ function createRoot(canvas, provider) {
15141
15337
  if (state.textureColorSpace !== textureColorSpace) state.set(() => ({ textureColorSpace }));
15142
15338
  lastConfiguredProps.textureColorSpace = textureColorSpace;
15143
15339
  }
15144
- const r3fProps = ["textureColorSpace"];
15340
+ const r3fProps = ["textureColorSpace", "primaryCanvas", "scheduler"];
15145
15341
  const constructorOnlyProps = ["samples", "antialias", "alpha", "canvas", "powerPreference"];
15146
15342
  const nonApplyProps = [...r3fProps, ...constructorOnlyProps];
15147
15343
  if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && !is.equ(glConfig, renderer, shallowLoose)) {
@@ -15151,7 +15347,7 @@ function createRoot(canvas, provider) {
15151
15347
  }
15152
15348
  applyProps(renderer, glProps);
15153
15349
  }
15154
- if (rendererConfig && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && state.renderer) {
15350
+ if (rendererConfig && !is.fun(rendererConfig) && !isRenderer(rendererConfig) && state.renderer && !borrowsRenderer) {
15155
15351
  const currentRenderer = state.renderer;
15156
15352
  if (!is.equ(rendererConfig, currentRenderer, shallowLoose)) {
15157
15353
  const rendererProps = {};
@@ -15294,8 +15490,11 @@ function createRoot(canvas, provider) {
15294
15490
  return this;
15295
15491
  },
15296
15492
  render(children) {
15493
+ if (_roots.get(canvas) !== root) return store;
15494
+ cancelUnmount();
15297
15495
  if (!configured && !pending) this.configure();
15298
15496
  pending.then(() => {
15497
+ if (_roots.get(canvas) !== root || root.unmountClaim) return;
15299
15498
  reconciler.updateContainer(
15300
15499
  /* @__PURE__ */ jsx(Provider, { store, children, onCreated, rootElement: canvas }),
15301
15500
  fiber,
@@ -15306,7 +15505,7 @@ function createRoot(canvas, provider) {
15306
15505
  return store;
15307
15506
  },
15308
15507
  unmount() {
15309
- unmountComponentAtNode(canvas);
15508
+ if (_roots.get(canvas) === root) unmountComponentAtNode(canvas);
15310
15509
  }
15311
15510
  };
15312
15511
  }
@@ -15326,46 +15525,52 @@ function Provider({
15326
15525
  }
15327
15526
  function unmountComponentAtNode(canvas, callback) {
15328
15527
  const root = _roots.get(canvas);
15329
- const fiber = root?.fiber;
15330
- if (fiber) {
15331
- const state = root?.store.getState();
15332
- if (state) {
15333
- state.internal.active = false;
15334
- const unregisterRoot = state.internal.unregisterRoot;
15335
- if (unregisterRoot) {
15336
- unregisterRoot();
15337
- state.internal.unregisterRoot = void 0;
15528
+ if (!root) return;
15529
+ const claim = root.unmountClaim = Symbol("unmount");
15530
+ root.store.getState().internal.active = false;
15531
+ reconciler.updateContainer(null, root.fiber, null, () => {
15532
+ if (root.unmountClaim !== claim) return;
15533
+ reconciler.updateContainer(null, root.fiber, null, () => {
15534
+ if (root.unmountClaim !== claim) return;
15535
+ if (root.ready.status === "pending") root.ready.then(teardown, teardown);
15536
+ else teardown();
15537
+ });
15538
+ });
15539
+ function teardown() {
15540
+ if (root.unmountClaim !== claim) return;
15541
+ root.unmountClaim = null;
15542
+ _roots.delete(canvas);
15543
+ const state = root.store.getState();
15544
+ const internal = state.internal;
15545
+ internal.active = false;
15546
+ const attempt = (step) => {
15547
+ try {
15548
+ return step();
15549
+ } catch (error) {
15550
+ console.warn("[R3F] Error while unmounting root; teardown may be incomplete:", error);
15338
15551
  }
15339
- }
15340
- reconciler.updateContainer(null, fiber, null, () => {
15341
- if (root?.store) detachRootExtensions(root.store);
15342
- if (state) {
15343
- setTimeout(() => {
15344
- try {
15345
- const renderer = state.internal.actualRenderer;
15346
- const unregisterPrimary = state.internal.unregisterPrimary;
15347
- if (unregisterPrimary) unregisterPrimary();
15348
- const canvasTarget = state.internal.canvasTarget;
15349
- if (state.internal.isSecondary && canvasTarget?.dispose) canvasTarget.dispose();
15350
- state.events.disconnect?.();
15351
- cleanupHelperGroup(root.store);
15352
- if (state.isLegacy && renderer) {
15353
- ;
15354
- renderer.renderLists?.dispose?.();
15355
- renderer.forceContextLoss?.();
15356
- }
15357
- if (!state.internal.isSecondary) {
15358
- if (renderer?.xr) state.xr.disconnect();
15359
- }
15360
- dispose(state.scene);
15361
- _roots.delete(canvas);
15362
- if (callback) callback(canvas);
15363
- } catch (error) {
15364
- console.warn("[R3F] Error while unmounting root; teardown may be incomplete:", error);
15365
- }
15366
- }, 500);
15552
+ };
15553
+ attempt(() => internal.unwatchDpr?.());
15554
+ internal.unwatchDpr = void 0;
15555
+ attempt(() => internal.unregisterRoot?.());
15556
+ internal.unregisterRoot = void 0;
15557
+ attempt(() => detachRootExtensions(root.store));
15558
+ attempt(() => internal.unregisterPrimary?.());
15559
+ attempt(() => state.events.disconnect?.());
15560
+ if (!internal.isSecondary && internal.actualRenderer?.xr && state.xr) attempt(() => state.xr.disconnect());
15561
+ attempt(() => cleanupHelperGroup(root.store));
15562
+ if (state.scene) attempt(() => dispose(state.scene));
15563
+ const canvasTarget = internal.canvasTarget;
15564
+ if (internal.isSecondary && canvasTarget?.dispose) attempt(() => canvasTarget.dispose());
15565
+ const released = attempt(() => internal.releaseRenderer?.());
15566
+ internal.releaseRenderer = void 0;
15567
+ if (callback) {
15568
+ if (released) {
15569
+ released.then(() => callback(canvas)).catch((error) => console.warn("[R3F] Error in unmount callback", error));
15570
+ } else {
15571
+ callback(canvas);
15367
15572
  }
15368
- });
15573
+ }
15369
15574
  }
15370
15575
  }
15371
15576
  function createPortal(children, container, state) {
@@ -15910,13 +16115,27 @@ function CanvasImpl({
15910
16115
  }
15911
16116
  };
15912
16117
  });
16118
+ const insertionMounted = React.useRef(false);
16119
+ React.useInsertionEffect(() => {
16120
+ insertionMounted.current = true;
16121
+ return () => {
16122
+ insertionMounted.current = false;
16123
+ queueMicrotask(() => {
16124
+ if (insertionMounted.current) return;
16125
+ const current = root.current;
16126
+ root.current = null;
16127
+ current?.unmount();
16128
+ });
16129
+ };
16130
+ }, []);
15913
16131
  React.useEffect(() => {
15914
16132
  const canvas = canvasRef.current;
15915
16133
  if (canvas) {
15916
16134
  return () => {
15917
16135
  if (canvas.isConnected) return;
15918
- unmountComponentAtNode(canvas);
16136
+ const current = root.current;
15919
16137
  root.current = null;
16138
+ current?.unmount();
15920
16139
  };
15921
16140
  }
15922
16141
  }, []);