@react-three/fiber 10.0.0-canary.ba354c7 → 10.0.0-canary.c385e9f
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/index.cjs +105 -53
- package/dist/index.d.cts +173 -24
- package/dist/index.d.mts +173 -24
- package/dist/index.d.ts +173 -24
- package/dist/index.mjs +105 -53
- package/dist/legacy.cjs +104 -57
- package/dist/legacy.d.cts +172 -27
- package/dist/legacy.d.mts +172 -27
- package/dist/legacy.d.ts +172 -27
- package/dist/legacy.mjs +104 -57
- package/dist/webgpu/index.cjs +168 -90
- package/dist/webgpu/index.d.cts +197 -88
- package/dist/webgpu/index.d.mts +197 -88
- package/dist/webgpu/index.d.ts +197 -88
- package/dist/webgpu/index.mjs +168 -90
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
const webgpu = require('three/webgpu');
|
|
4
4
|
const three = require('three');
|
|
5
|
-
const Inspector_js = require('three/addons/inspector/Inspector.js');
|
|
6
5
|
const jsxRuntime = require('react/jsx-runtime');
|
|
7
6
|
const React = require('react');
|
|
8
7
|
const useMeasure = require('react-use-measure');
|
|
@@ -52,18 +51,22 @@ const Tb__default = /*#__PURE__*/_interopDefaultCompat(Tb);
|
|
|
52
51
|
|
|
53
52
|
const R3F_BUILD_LEGACY = true;
|
|
54
53
|
const R3F_BUILD_WEBGPU = true;
|
|
54
|
+
async function loadInspector() {
|
|
55
|
+
const { Inspector } = await import('three/addons/inspector/Inspector.js');
|
|
56
|
+
return Inspector;
|
|
57
|
+
}
|
|
55
58
|
|
|
56
59
|
const THREE = /*#__PURE__*/_mergeNamespaces({
|
|
57
60
|
__proto__: null,
|
|
58
61
|
CubeRenderTarget: webgpu.CubeRenderTarget,
|
|
59
62
|
CubeRenderTargetCompat: webgpu.CubeRenderTarget,
|
|
60
|
-
Inspector: Inspector_js.Inspector,
|
|
61
63
|
R3F_BUILD_LEGACY: R3F_BUILD_LEGACY,
|
|
62
64
|
R3F_BUILD_WEBGPU: R3F_BUILD_WEBGPU,
|
|
63
65
|
RenderTargetCompat: webgpu.RenderTarget,
|
|
64
66
|
WebGLCubeRenderTarget: three.WebGLCubeRenderTarget,
|
|
65
67
|
WebGLRenderTarget: three.WebGLRenderTarget,
|
|
66
|
-
WebGLRenderer: three.WebGLRenderer
|
|
68
|
+
WebGLRenderer: three.WebGLRenderer,
|
|
69
|
+
loadInspector: loadInspector
|
|
67
70
|
}, [webgpu__namespace]);
|
|
68
71
|
|
|
69
72
|
const primaryRegistry = /* @__PURE__ */ new Map();
|
|
@@ -922,7 +925,7 @@ const createStore = (invalidate, advance) => {
|
|
|
922
925
|
size: newSize,
|
|
923
926
|
viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, newSize) }
|
|
924
927
|
}));
|
|
925
|
-
|
|
928
|
+
get().invalidate();
|
|
926
929
|
}
|
|
927
930
|
}
|
|
928
931
|
return;
|
|
@@ -937,15 +940,16 @@ const createStore = (invalidate, advance) => {
|
|
|
937
940
|
viewport: { ...s.viewport, ...getCurrentViewport(state2.camera, defaultTarget, size) },
|
|
938
941
|
_sizeImperative: true
|
|
939
942
|
}));
|
|
940
|
-
|
|
943
|
+
get().invalidate();
|
|
941
944
|
},
|
|
942
945
|
setDpr: (dpr) => set((state2) => {
|
|
943
946
|
const resolved = calculateDpr(dpr);
|
|
944
947
|
return { viewport: { ...state2.viewport, dpr: resolved, initialDpr: state2.viewport.initialDpr || resolved } };
|
|
945
948
|
}),
|
|
946
949
|
setFrameloop: (frameloop = "always") => {
|
|
950
|
+
const rootId = get().internal.rootId;
|
|
951
|
+
if (rootId) scheduler.getScheduler().setRootFrameloop(rootId, frameloop);
|
|
947
952
|
set(() => ({ frameloop }));
|
|
948
|
-
scheduler.getScheduler().frameloop = frameloop;
|
|
949
953
|
},
|
|
950
954
|
setError: (error) => set(() => ({ error })),
|
|
951
955
|
error: null,
|
|
@@ -1061,17 +1065,10 @@ const createStore = (invalidate, advance) => {
|
|
|
1061
1065
|
oldSize = size;
|
|
1062
1066
|
oldDpr = viewport.dpr;
|
|
1063
1067
|
updateCamera(camera, size);
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
if (viewport.dpr > 0) actualRenderer.setPixelRatio(viewport.dpr);
|
|
1069
|
-
actualRenderer.setSize(size.width, size.height, false);
|
|
1070
|
-
if (canvasTarget) {
|
|
1071
|
-
if (viewport.dpr > 0) canvasTarget.setPixelRatio(viewport.dpr);
|
|
1072
|
-
canvasTarget.setSize(size.width, size.height, false);
|
|
1073
|
-
}
|
|
1074
|
-
}
|
|
1068
|
+
const resizeTarget = canvasTarget ?? actualRenderer;
|
|
1069
|
+
if (viewport.dpr > 0) resizeTarget.setPixelRatio(viewport.dpr);
|
|
1070
|
+
resizeTarget.setSize(size.width, size.height, false);
|
|
1071
|
+
internal.canvasTargetSizeDirty = true;
|
|
1075
1072
|
}
|
|
1076
1073
|
if (camera !== oldCamera) {
|
|
1077
1074
|
oldCamera = camera;
|
|
@@ -1133,7 +1130,11 @@ function useLoader(loader, input, extensions, onProgress, cacheKey) {
|
|
|
1133
1130
|
const results = cacheKeys.map(
|
|
1134
1131
|
(key, index) => suspendReact.suspend(() => fn(loader, inputs[index]), [loader, key], { equal: is.equ })
|
|
1135
1132
|
);
|
|
1136
|
-
|
|
1133
|
+
const stableRef = React.useRef(null);
|
|
1134
|
+
const previous = stableRef.current;
|
|
1135
|
+
const stable = previous !== null && previous.length === results.length && previous.every((value, i) => value === results[i]) ? previous : results;
|
|
1136
|
+
stableRef.current = stable;
|
|
1137
|
+
return Array.isArray(input) ? stable : stable[0];
|
|
1137
1138
|
}
|
|
1138
1139
|
useLoader.preload = function(loader, input, extensions, onProgress, cacheKey) {
|
|
1139
1140
|
const inputs = Array.isArray(input) ? input : [input];
|
|
@@ -1253,6 +1254,13 @@ function useFrame(callback, priorityOrOptions) {
|
|
|
1253
1254
|
* Use for controlling the entire frame loop, adding phases, etc.
|
|
1254
1255
|
*/
|
|
1255
1256
|
scheduler: scheduler2,
|
|
1257
|
+
/**
|
|
1258
|
+
* The root that currently owns this job. Resolve on access because ambient
|
|
1259
|
+
* jobs can be adopted by a Canvas after the controls object is created.
|
|
1260
|
+
*/
|
|
1261
|
+
get rootId() {
|
|
1262
|
+
return scheduler.getScheduler().getJobRootId(id);
|
|
1263
|
+
},
|
|
1256
1264
|
/**
|
|
1257
1265
|
* Manually step this job only.
|
|
1258
1266
|
* Bypasses FPS limiting - always runs.
|
|
@@ -1269,6 +1277,14 @@ function useFrame(callback, priorityOrOptions) {
|
|
|
1269
1277
|
stepAll: (timestamp) => {
|
|
1270
1278
|
scheduler.getScheduler().step(timestamp);
|
|
1271
1279
|
},
|
|
1280
|
+
/**
|
|
1281
|
+
* Request frames for this job's owning root without waking sibling roots.
|
|
1282
|
+
*/
|
|
1283
|
+
invalidate: (frames, stackFrames) => {
|
|
1284
|
+
const scheduler3 = scheduler.getScheduler();
|
|
1285
|
+
const rootId = scheduler3.getJobRootId(id);
|
|
1286
|
+
if (rootId) scheduler3.invalidateRoot(rootId, frames, stackFrames);
|
|
1287
|
+
},
|
|
1272
1288
|
/**
|
|
1273
1289
|
* Pause this job (set enabled=false).
|
|
1274
1290
|
* Job remains registered but won't run.
|
|
@@ -1298,6 +1314,11 @@ function getUrls(input) {
|
|
|
1298
1314
|
if (Array.isArray(input)) return input;
|
|
1299
1315
|
return Object.values(input);
|
|
1300
1316
|
}
|
|
1317
|
+
function inputSignature(input) {
|
|
1318
|
+
if (typeof input === "string") return input;
|
|
1319
|
+
if (Array.isArray(input)) return input.join("\0");
|
|
1320
|
+
return Object.keys(input).sort().map((key) => `${key}\0${input[key]}`).join("");
|
|
1321
|
+
}
|
|
1301
1322
|
function allUrlsCached(urls, textureCache) {
|
|
1302
1323
|
return urls.every((url) => textureCache.has(url));
|
|
1303
1324
|
}
|
|
@@ -1323,18 +1344,19 @@ function useTexture(input, optionsOrOnLoad) {
|
|
|
1323
1344
|
const onLoadRef = React.useRef(onLoad);
|
|
1324
1345
|
onLoadRef.current = onLoad;
|
|
1325
1346
|
const onLoadCalledForRef = React.useRef(null);
|
|
1326
|
-
const
|
|
1347
|
+
const inputKey = React.useMemo(() => inputSignature(input), [input]);
|
|
1348
|
+
const stableInput = React.useMemo(() => input, [inputKey]);
|
|
1349
|
+
const urls = React.useMemo(() => getUrls(stableInput), [stableInput]);
|
|
1327
1350
|
const cachedResult = React.useMemo(() => {
|
|
1328
1351
|
if (!cache) return null;
|
|
1329
1352
|
const textures = store.getState().textures;
|
|
1330
1353
|
if (!allUrlsCached(urls, textures)) return null;
|
|
1331
|
-
return buildFromCache(
|
|
1332
|
-
}, [cache, urls,
|
|
1354
|
+
return buildFromCache(stableInput, textures);
|
|
1355
|
+
}, [cache, urls, stableInput, store]);
|
|
1333
1356
|
const loadedTextures = useLoader(
|
|
1334
1357
|
webgpu.TextureLoader,
|
|
1335
|
-
IsObject(
|
|
1358
|
+
IsObject(stableInput) ? Object.values(stableInput) : stableInput
|
|
1336
1359
|
);
|
|
1337
|
-
const inputKey = urls.join("\0");
|
|
1338
1360
|
React.useLayoutEffect(() => {
|
|
1339
1361
|
if (cachedResult) return;
|
|
1340
1362
|
if (onLoadCalledForRef.current === inputKey) return;
|
|
@@ -1361,28 +1383,28 @@ function useTexture(input, optionsOrOnLoad) {
|
|
|
1361
1383
|
}, [renderer, loadedTextures, cachedResult]);
|
|
1362
1384
|
const mappedTextures = React.useMemo(() => {
|
|
1363
1385
|
if (cachedResult) return cachedResult;
|
|
1364
|
-
if (IsObject(
|
|
1386
|
+
if (IsObject(stableInput)) {
|
|
1365
1387
|
const keyed = {};
|
|
1366
1388
|
const textureArray = loadedTextures;
|
|
1367
1389
|
let i = 0;
|
|
1368
|
-
for (const key in
|
|
1390
|
+
for (const key in stableInput) keyed[key] = textureArray[i++];
|
|
1369
1391
|
return keyed;
|
|
1370
1392
|
} else {
|
|
1371
1393
|
return loadedTextures;
|
|
1372
1394
|
}
|
|
1373
|
-
}, [
|
|
1395
|
+
}, [stableInput, loadedTextures, cachedResult]);
|
|
1374
1396
|
React.useEffect(() => {
|
|
1375
1397
|
if (!cache) return;
|
|
1376
1398
|
const urlTextureMap = [];
|
|
1377
|
-
if (typeof
|
|
1378
|
-
urlTextureMap.push([
|
|
1379
|
-
} else if (Array.isArray(
|
|
1399
|
+
if (typeof stableInput === "string") {
|
|
1400
|
+
urlTextureMap.push([stableInput, mappedTextures]);
|
|
1401
|
+
} else if (Array.isArray(stableInput)) {
|
|
1380
1402
|
const textureArray = mappedTextures;
|
|
1381
|
-
|
|
1382
|
-
} else if (IsObject(
|
|
1403
|
+
stableInput.forEach((url, i) => urlTextureMap.push([url, textureArray[i]]));
|
|
1404
|
+
} else if (IsObject(stableInput)) {
|
|
1383
1405
|
const textureRecord = mappedTextures;
|
|
1384
|
-
for (const key in
|
|
1385
|
-
const url =
|
|
1406
|
+
for (const key in stableInput) {
|
|
1407
|
+
const url = stableInput[key];
|
|
1386
1408
|
urlTextureMap.push([url, textureRecord[key]]);
|
|
1387
1409
|
}
|
|
1388
1410
|
}
|
|
@@ -1411,7 +1433,7 @@ function useTexture(input, optionsOrOnLoad) {
|
|
|
1411
1433
|
}
|
|
1412
1434
|
return { _textureRefs: refs };
|
|
1413
1435
|
});
|
|
1414
|
-
}, [cache,
|
|
1436
|
+
}, [cache, stableInput, mappedTextures, store]);
|
|
1415
1437
|
return mappedTextures;
|
|
1416
1438
|
}
|
|
1417
1439
|
useTexture.preload = (url) => useLoader.preload(webgpu.TextureLoader, url);
|
|
@@ -1682,13 +1704,19 @@ function addTail(callback) {
|
|
|
1682
1704
|
return scheduler.getScheduler().onIdle(callback);
|
|
1683
1705
|
}
|
|
1684
1706
|
function invalidate(state, frames = 1, stackFrames = false) {
|
|
1685
|
-
|
|
1707
|
+
const rootId = state?.internal?.rootId;
|
|
1708
|
+
const scheduler$1 = scheduler.getScheduler();
|
|
1709
|
+
if (rootId) scheduler$1.invalidateRoot(rootId, frames, stackFrames);
|
|
1710
|
+
else scheduler$1.invalidate(frames, stackFrames);
|
|
1686
1711
|
}
|
|
1687
|
-
function advance(timestamp) {
|
|
1688
|
-
|
|
1712
|
+
function advance(timestamp, runGlobalEffects, state) {
|
|
1713
|
+
const rootId = state?.internal?.rootId;
|
|
1714
|
+
const scheduler$1 = scheduler.getScheduler();
|
|
1715
|
+
if (rootId) scheduler$1.stepRoot(rootId, timestamp);
|
|
1716
|
+
else scheduler$1.step(timestamp);
|
|
1689
1717
|
}
|
|
1690
1718
|
|
|
1691
|
-
const version = "10.0.0-alpha.
|
|
1719
|
+
const version = "10.0.0-alpha.4";
|
|
1692
1720
|
const packageData = {
|
|
1693
1721
|
version: version};
|
|
1694
1722
|
|
|
@@ -14347,7 +14375,7 @@ function createRoot(canvas) {
|
|
|
14347
14375
|
const handleXRFrame = (timestamp, _frame) => {
|
|
14348
14376
|
const state2 = store.getState();
|
|
14349
14377
|
if (state2.frameloop === "never") return;
|
|
14350
|
-
advance(timestamp);
|
|
14378
|
+
advance(timestamp, true, state2);
|
|
14351
14379
|
};
|
|
14352
14380
|
const actualRenderer = state.internal.actualRenderer;
|
|
14353
14381
|
const handleSessionChange = () => {
|
|
@@ -14355,7 +14383,7 @@ function createRoot(canvas) {
|
|
|
14355
14383
|
const renderer2 = state2.internal.actualRenderer;
|
|
14356
14384
|
actualRenderer.xr.enabled = actualRenderer.xr.isPresenting;
|
|
14357
14385
|
renderer2.xr.setAnimationLoop(renderer2.xr.isPresenting ? handleXRFrame : null);
|
|
14358
|
-
if (!renderer2.xr.isPresenting) invalidate();
|
|
14386
|
+
if (!renderer2.xr.isPresenting) invalidate(state2);
|
|
14359
14387
|
};
|
|
14360
14388
|
const xr = {
|
|
14361
14389
|
connect() {
|
|
@@ -14437,7 +14465,11 @@ function createRoot(canvas) {
|
|
|
14437
14465
|
const newRootId = canvasId || scheduler$1.generateRootId();
|
|
14438
14466
|
const unregisterRoot = scheduler$1.registerRoot(newRootId, {
|
|
14439
14467
|
getState: () => store.getState(),
|
|
14440
|
-
onError: (err) => store.getState().setError(err)
|
|
14468
|
+
onError: (err) => store.getState().setError(err),
|
|
14469
|
+
frameloop: store.getState().frameloop,
|
|
14470
|
+
before: schedulerConfig?.before,
|
|
14471
|
+
after: schedulerConfig?.after,
|
|
14472
|
+
order: schedulerConfig?.order
|
|
14441
14473
|
});
|
|
14442
14474
|
const unregisterCanvasTarget = scheduler$1.register(
|
|
14443
14475
|
() => {
|
|
@@ -14445,6 +14477,10 @@ function createRoot(canvas) {
|
|
|
14445
14477
|
if (state2.internal.isMultiCanvas && state2.internal.canvasTarget) {
|
|
14446
14478
|
const renderer2 = state2.internal.actualRenderer;
|
|
14447
14479
|
renderer2.setCanvasTarget(state2.internal.canvasTarget);
|
|
14480
|
+
if (state2.internal.canvasTargetSizeDirty) {
|
|
14481
|
+
state2.internal.canvasTargetSizeDirty = false;
|
|
14482
|
+
renderer2.backend?.updateSize?.();
|
|
14483
|
+
}
|
|
14448
14484
|
}
|
|
14449
14485
|
},
|
|
14450
14486
|
{
|
|
@@ -14513,8 +14549,7 @@ function createRoot(canvas) {
|
|
|
14513
14549
|
phase: "render",
|
|
14514
14550
|
system: true,
|
|
14515
14551
|
// Internal flag: this is a system job, not user-controlled
|
|
14516
|
-
//
|
|
14517
|
-
...schedulerConfig?.after && { after: schedulerConfig.after },
|
|
14552
|
+
// FPS throttles only the default render job; Canvas ordering belongs to the root.
|
|
14518
14553
|
...schedulerConfig?.fps && { fps: schedulerConfig.fps }
|
|
14519
14554
|
}
|
|
14520
14555
|
);
|
|
@@ -14533,8 +14568,21 @@ function createRoot(canvas) {
|
|
|
14533
14568
|
scheduler: scheduler$1
|
|
14534
14569
|
}
|
|
14535
14570
|
}));
|
|
14571
|
+
} else {
|
|
14572
|
+
const constraintsChanged = !is.equ(schedulerConfig?.before, lastConfiguredProps.schedulerBefore, shallowLoose) || !is.equ(schedulerConfig?.after, lastConfiguredProps.schedulerAfter, shallowLoose);
|
|
14573
|
+
if (constraintsChanged) {
|
|
14574
|
+
scheduler$1.setRootConstraints(rootId, {
|
|
14575
|
+
before: schedulerConfig?.before,
|
|
14576
|
+
after: schedulerConfig?.after
|
|
14577
|
+
});
|
|
14578
|
+
}
|
|
14579
|
+
if (schedulerConfig?.order !== lastConfiguredProps.schedulerOrder) {
|
|
14580
|
+
scheduler$1.setRootOrder(rootId, schedulerConfig?.order ?? 0);
|
|
14581
|
+
}
|
|
14536
14582
|
}
|
|
14537
|
-
|
|
14583
|
+
lastConfiguredProps.schedulerBefore = schedulerConfig?.before;
|
|
14584
|
+
lastConfiguredProps.schedulerAfter = schedulerConfig?.after;
|
|
14585
|
+
lastConfiguredProps.schedulerOrder = schedulerConfig?.order;
|
|
14538
14586
|
onCreated = onCreatedCallback;
|
|
14539
14587
|
configured = true;
|
|
14540
14588
|
resolve();
|
|
@@ -14576,14 +14624,19 @@ function unmountComponentAtNode(canvas, callback) {
|
|
|
14576
14624
|
const fiber = root?.fiber;
|
|
14577
14625
|
if (fiber) {
|
|
14578
14626
|
const state = root?.store.getState();
|
|
14579
|
-
if (state)
|
|
14627
|
+
if (state) {
|
|
14628
|
+
state.internal.active = false;
|
|
14629
|
+
const unregisterRoot = state.internal.unregisterRoot;
|
|
14630
|
+
if (unregisterRoot) {
|
|
14631
|
+
unregisterRoot();
|
|
14632
|
+
state.internal.unregisterRoot = void 0;
|
|
14633
|
+
}
|
|
14634
|
+
}
|
|
14580
14635
|
reconciler.updateContainer(null, fiber, null, () => {
|
|
14581
14636
|
if (state) {
|
|
14582
14637
|
setTimeout(() => {
|
|
14583
14638
|
try {
|
|
14584
14639
|
const renderer = state.internal.actualRenderer;
|
|
14585
|
-
const unregisterRoot = state.internal.unregisterRoot;
|
|
14586
|
-
if (unregisterRoot) unregisterRoot();
|
|
14587
14640
|
const unregisterPrimary = state.internal.unregisterPrimary;
|
|
14588
14641
|
if (unregisterPrimary) unregisterPrimary();
|
|
14589
14642
|
const canvasTarget = state.internal.canvasTarget;
|
|
@@ -15031,9 +15084,9 @@ function CanvasImpl({
|
|
|
15031
15084
|
React__namespace.useMemo(() => extend(THREE), []);
|
|
15032
15085
|
const Bridge = useBridge();
|
|
15033
15086
|
const backgroundProps = React__namespace.useMemo(() => parseBackground(background), [background]);
|
|
15034
|
-
const
|
|
15087
|
+
const [hasInitialSize, setHasInitialSize] = React__namespace.useState(false);
|
|
15035
15088
|
const measureConfig = React__namespace.useMemo(() => {
|
|
15036
|
-
if (!
|
|
15089
|
+
if (!hasInitialSize) {
|
|
15037
15090
|
return {
|
|
15038
15091
|
...resize,
|
|
15039
15092
|
scroll: resize?.scroll ?? true,
|
|
@@ -15045,7 +15098,7 @@ function CanvasImpl({
|
|
|
15045
15098
|
debounce: { scroll: 50, resize: 0 },
|
|
15046
15099
|
...resize
|
|
15047
15100
|
};
|
|
15048
|
-
}, [
|
|
15101
|
+
}, [hasInitialSize, resize]);
|
|
15049
15102
|
const [containerRef, containerRect] = useMeasure__default(measureConfig);
|
|
15050
15103
|
const effectiveSize = React__namespace.useMemo(() => {
|
|
15051
15104
|
let w = width ?? containerRect.width;
|
|
@@ -15061,9 +15114,7 @@ function CanvasImpl({
|
|
|
15061
15114
|
left: containerRect.left
|
|
15062
15115
|
};
|
|
15063
15116
|
}, [width, height, containerRect, forceEven]);
|
|
15064
|
-
if (!
|
|
15065
|
-
hasInitialSizeRef.current = true;
|
|
15066
|
-
}
|
|
15117
|
+
if (!hasInitialSize && effectiveSize.width > 0 && effectiveSize.height > 0) setHasInitialSize(true);
|
|
15067
15118
|
const canvasRef = React__namespace.useRef(null);
|
|
15068
15119
|
const divRef = React__namespace.useRef(null);
|
|
15069
15120
|
React__namespace.useImperativeHandle(ref, () => canvasRef.current);
|
|
@@ -15177,6 +15228,7 @@ function CanvasImpl({
|
|
|
15177
15228
|
const canvas = canvasRef.current;
|
|
15178
15229
|
if (canvas) {
|
|
15179
15230
|
return () => {
|
|
15231
|
+
if (canvas.isConnected) return;
|
|
15180
15232
|
unmountComponentAtNode(canvas);
|
|
15181
15233
|
root.current = null;
|
|
15182
15234
|
};
|