@react-three/fiber 8.2.0 → 9.0.0-alpha.0

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 (27) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/declarations/src/core/events.d.ts +69 -69
  3. package/dist/declarations/src/core/hooks.d.ts +23 -21
  4. package/dist/declarations/src/core/index.d.ts +61 -57
  5. package/dist/declarations/src/core/loop.d.ts +13 -13
  6. package/dist/declarations/src/core/renderer.d.ts +51 -51
  7. package/dist/declarations/src/core/stages.d.ts +59 -0
  8. package/dist/declarations/src/core/store.d.ts +110 -95
  9. package/dist/declarations/src/core/utils.d.ts +81 -82
  10. package/dist/declarations/src/index.d.ts +12 -11
  11. package/dist/declarations/src/native/Canvas.d.ts +8 -8
  12. package/dist/declarations/src/native/events.d.ts +4 -4
  13. package/dist/declarations/src/native/polyfills.d.ts +1 -1
  14. package/dist/declarations/src/native.d.ts +10 -10
  15. package/dist/declarations/src/three-types.d.ts +331 -331
  16. package/dist/declarations/src/web/Canvas.d.ts +9 -9
  17. package/dist/declarations/src/web/events.d.ts +4 -4
  18. package/dist/{index-ca47b633.cjs.prod.js → index-b12f488b.cjs.prod.js} +290 -69
  19. package/dist/{index-0499a96a.cjs.dev.js → index-b3be5a63.cjs.dev.js} +290 -69
  20. package/dist/{index-6279214a.esm.js → index-e27d4a05.esm.js} +287 -70
  21. package/dist/react-three-fiber.cjs.dev.js +7 -1
  22. package/dist/react-three-fiber.cjs.prod.js +7 -1
  23. package/dist/react-three-fiber.esm.js +4 -2
  24. package/native/dist/react-three-fiber-native.cjs.dev.js +4 -1
  25. package/native/dist/react-three-fiber-native.cjs.prod.js +4 -1
  26. package/native/dist/react-three-fiber-native.esm.js +4 -2
  27. package/package.json +6 -6
@@ -1,9 +1,9 @@
1
- import * as React from 'react';
2
- import type { Options as ResizeOptions } from 'react-use-measure';
3
- import { RenderProps } from '../core';
4
- export interface Props extends Omit<RenderProps<HTMLCanvasElement>, 'size'>, React.HTMLAttributes<HTMLDivElement> {
5
- children: React.ReactNode;
6
- fallback?: React.ReactNode;
7
- resize?: ResizeOptions;
8
- }
9
- export declare const Canvas: React.ForwardRefExoticComponent<Props & React.RefAttributes<HTMLCanvasElement>>;
1
+ import * as React from 'react';
2
+ import type { Options as ResizeOptions } from 'react-use-measure';
3
+ import { RenderProps } from '../core';
4
+ export interface Props extends Omit<RenderProps<HTMLCanvasElement>, 'size'>, React.HTMLAttributes<HTMLDivElement> {
5
+ children: React.ReactNode;
6
+ fallback?: React.ReactNode;
7
+ resize?: ResizeOptions;
8
+ }
9
+ export declare const Canvas: React.ForwardRefExoticComponent<Props & React.RefAttributes<HTMLCanvasElement>>;
@@ -1,4 +1,4 @@
1
- import { UseBoundStore } from 'zustand';
2
- import { RootState } from '../core/store';
3
- import { EventManager } from '../core/events';
4
- export declare function createPointerEvents(store: UseBoundStore<RootState>): EventManager<HTMLElement>;
1
+ import { UseBoundStore } from 'zustand';
2
+ import { RootState } from '../core/store';
3
+ import { EventManager } from '../core/events';
4
+ export declare function createPointerEvents(store: UseBoundStore<RootState>): EventManager<HTMLElement>;
@@ -37,12 +37,19 @@ var threeTypes = /*#__PURE__*/Object.freeze({
37
37
  __proto__: null
38
38
  });
39
39
 
40
- const isOrthographicCamera = def => def && def.isOrthographicCamera; // React currently throws a warning when using useLayoutEffect on the server.
41
- // To get around it, we can conditionally useEffect on the server (no-op) and
42
- // useLayoutEffect on the client.
40
+ var _window$document, _window$navigator;
41
+ const isOrthographicCamera = def => def && def.isOrthographicCamera;
42
+ /**
43
+ * An SSR-friendly useLayoutEffect.
44
+ *
45
+ * React currently throws a warning when using useLayoutEffect on the server.
46
+ * To get around it, we can conditionally useEffect on the server (no-op) and
47
+ * useLayoutEffect elsewhere.
48
+ *
49
+ * @see https://github.com/facebook/react/issues/14927
50
+ */
43
51
 
44
- const isSSR = typeof window === 'undefined' || !window.navigator || /ServerSideRendering|^Deno\//.test(window.navigator.userAgent);
45
- const useIsomorphicLayoutEffect = isSSR ? React__namespace.useEffect : React__namespace.useLayoutEffect;
52
+ const useIsomorphicLayoutEffect = typeof window !== 'undefined' && ((_window$document = window.document) != null && _window$document.createElement || ((_window$navigator = window.navigator) == null ? void 0 : _window$navigator.product) === 'ReactNative') ? React__namespace.useLayoutEffect : React__namespace.useEffect;
46
53
  function useMutableCallback(fn) {
47
54
  const ref = React__namespace.useRef(fn);
48
55
  useIsomorphicLayoutEffect(() => void (ref.current = fn), [fn]);
@@ -358,12 +365,7 @@ function applyProps$1(instance, data) {
358
365
 
359
366
  if (!isColor && targetProp.setScalar) targetProp.setScalar(value); // Layers have no copy function, we must therefore copy the mask property
360
367
  else if (targetProp instanceof THREE__namespace.Layers && value instanceof THREE__namespace.Layers) targetProp.mask = value.mask; // Otherwise just set ...
361
- else targetProp.set(value); // For versions of three which don't support THREE.ColorManagement,
362
- // Auto-convert sRGB colors
363
- // https://github.com/pmndrs/react-three-fiber/issues/344
364
-
365
- const supportsColorManagement = ('ColorManagement' in THREE__namespace);
366
- if (!supportsColorManagement && !rootState.linear && isColor) targetProp.convertSRGBToLinear();
368
+ else targetProp.set(value);
367
369
  } // Else, just overwrite the value
368
370
 
369
371
  } else {
@@ -418,15 +420,6 @@ function updateCamera(camera, size) {
418
420
  camera.updateMatrixWorld();
419
421
  }
420
422
  }
421
- /**
422
- * Safely sets a deeply-nested value on an object.
423
- */
424
-
425
- function setDeep(obj, value, keys) {
426
- const key = keys.pop();
427
- const target = keys.reduce((acc, key) => acc[key], obj);
428
- return target[key] = value;
429
- }
430
423
 
431
424
  function makeId(event) {
432
425
  return (event.eventObject || event.object).uuid + '/' + event.index + event.instanceId;
@@ -1063,7 +1056,10 @@ function createRenderer(_roots, _getEventPriority) {
1063
1056
  }
1064
1057
  }
1065
1058
  });
1066
- }
1059
+ } // Don't handle text instances, warn on undefined behavior
1060
+
1061
+
1062
+ const handleTextInstance = () => console.warn('Text is not allowed in the R3F tree! This could be stray whitespace or characters.');
1067
1063
 
1068
1064
  const reconciler = Reconciler__default["default"]({
1069
1065
  createInstance,
@@ -1077,13 +1073,20 @@ function createRenderer(_roots, _getEventPriority) {
1077
1073
  supportsHydration: false,
1078
1074
  noTimeout: -1,
1079
1075
  appendChildToContainer: (container, child) => {
1076
+ if (!child) return;
1080
1077
  const scene = container.getState().scene; // Link current root to the default scene
1081
1078
 
1082
1079
  scene.__r3f.root = container;
1083
1080
  appendChild(scene, child);
1084
1081
  },
1085
- removeChildFromContainer: (container, child) => removeChild(container.getState().scene, child),
1086
- insertInContainerBefore: (container, child, beforeChild) => insertBefore(container.getState().scene, child, beforeChild),
1082
+ removeChildFromContainer: (container, child) => {
1083
+ if (!child) return;
1084
+ removeChild(container.getState().scene, child);
1085
+ },
1086
+ insertInContainerBefore: (container, child, beforeChild) => {
1087
+ if (!child || !beforeChild) return;
1088
+ insertBefore(container.getState().scene, child, beforeChild);
1089
+ },
1087
1090
  getRootHostContext: () => null,
1088
1091
  getChildHostContext: parentHostContext => parentHostContext,
1089
1092
 
@@ -1175,13 +1178,9 @@ function createRenderer(_roots, _getEventPriority) {
1175
1178
  invalidateInstance(instance);
1176
1179
  },
1177
1180
 
1178
- createTextInstance: () => {
1179
- throw new Error('Text is not allowed in the R3F tree.');
1180
- },
1181
- hideTextInstance: () => {
1182
- throw new Error('Text is not allowed in the R3F tree.');
1183
- },
1184
- unhideTextInstance: () => {},
1181
+ createTextInstance: handleTextInstance,
1182
+ hideTextInstance: handleTextInstance,
1183
+ unhideTextInstance: handleTextInstance,
1185
1184
  // https://github.com/pmndrs/react-three-fiber/pull/2360#discussion_r916356874
1186
1185
  // @ts-ignore
1187
1186
  getCurrentEventPriority: () => _getEventPriority ? _getEventPriority() : constants.DefaultEventPriority,
@@ -1199,6 +1198,7 @@ function createRenderer(_roots, _getEventPriority) {
1199
1198
  };
1200
1199
  }
1201
1200
 
1201
+ // Keys that shouldn't be copied between R3F stores
1202
1202
  const privateKeys = ['set', 'get', 'setSize', 'setFrameloop', 'setDpr', 'events', 'invalidate', 'advance', 'size', 'viewport'];
1203
1203
  const isRenderer = def => !!(def != null && def.render);
1204
1204
  const context = /*#__PURE__*/React__namespace.createContext(null);
@@ -1346,8 +1346,14 @@ const createStore = (invalidate, advance) => {
1346
1346
  }
1347
1347
  };
1348
1348
  }),
1349
- setFrameloop: (frameloop = 'always') => {
1350
- const clock = get().clock; // if frameloop === "never" clock.elapsedTime is updated using advance(timestamp)
1349
+ setFrameloop: frameloop => {
1350
+ var _frameloop$mode, _frameloop$render, _frameloop$maxDelta;
1351
+
1352
+ const state = get();
1353
+ const mode = typeof frameloop === 'string' ? frameloop : (frameloop == null ? void 0 : frameloop.mode) === 'auto' ? 'always' : (_frameloop$mode = frameloop == null ? void 0 : frameloop.mode) != null ? _frameloop$mode : state.frameloop;
1354
+ const render = typeof frameloop === 'string' ? state.internal.render : (_frameloop$render = frameloop == null ? void 0 : frameloop.render) != null ? _frameloop$render : state.internal.render;
1355
+ const maxDelta = typeof frameloop === 'string' ? state.internal.maxDelta : (_frameloop$maxDelta = frameloop == null ? void 0 : frameloop.maxDelta) != null ? _frameloop$maxDelta : state.internal.maxDelta;
1356
+ const clock = state.clock; // if frameloop === "never" clock.elapsedTime is updated using advance(timestamp)
1351
1357
 
1352
1358
  clock.stop();
1353
1359
  clock.elapsedTime = 0;
@@ -1358,28 +1364,44 @@ const createStore = (invalidate, advance) => {
1358
1364
  }
1359
1365
 
1360
1366
  set(() => ({
1361
- frameloop
1367
+ frameloop: mode,
1368
+ internal: { ...state.internal,
1369
+ render,
1370
+ maxDelta
1371
+ }
1362
1372
  }));
1363
1373
  },
1364
1374
  previousRoot: undefined,
1365
1375
  internal: {
1366
- active: false,
1367
- priority: 0,
1368
- frames: 0,
1369
- lastEvent: /*#__PURE__*/React__namespace.createRef(),
1376
+ // Events
1370
1377
  interaction: [],
1371
1378
  hovered: new Map(),
1372
1379
  subscribers: [],
1373
1380
  initialClick: [0, 0],
1374
1381
  initialHits: [],
1375
1382
  capturedMap: new Map(),
1383
+ lastEvent: /*#__PURE__*/React__namespace.createRef(),
1384
+ // Updates
1385
+ active: false,
1386
+ frames: 0,
1387
+ stages: [],
1388
+ render: 'auto',
1389
+ maxDelta: 1 / 10,
1390
+ priority: 0,
1376
1391
  subscribe: (ref, priority, store) => {
1377
- const internal = get().internal; // If this subscription was given a priority, it takes rendering into its own hands
1392
+ const state = get();
1393
+ const internal = state.internal; // If this subscription was given a priority, it takes rendering into its own hands
1378
1394
  // For that reason we switch off automatic rendering and increase the manual flag
1379
1395
  // As long as this flag is positive there can be no internal rendering at all
1380
1396
  // because there could be multiple render subscriptions
1381
1397
 
1382
- internal.priority = internal.priority + (priority > 0 ? 1 : 0);
1398
+ internal.priority = internal.priority + (priority > 0 ? 1 : 0); // We use the render flag and deprecate priority
1399
+
1400
+ if (internal.priority && state.internal.render === 'auto') set(() => ({
1401
+ internal: { ...state.internal,
1402
+ render: 'manual'
1403
+ }
1404
+ }));
1383
1405
  internal.subscribers.push({
1384
1406
  ref,
1385
1407
  priority,
@@ -1389,11 +1411,18 @@ const createStore = (invalidate, advance) => {
1389
1411
 
1390
1412
  internal.subscribers = internal.subscribers.sort((a, b) => a.priority - b.priority);
1391
1413
  return () => {
1392
- const internal = get().internal;
1414
+ const state = get();
1415
+ const internal = state.internal;
1393
1416
 
1394
1417
  if (internal != null && internal.subscribers) {
1395
1418
  // Decrease manual flag if this subscription had a priority
1396
- internal.priority = internal.priority - (priority > 0 ? 1 : 0); // Remove subscriber from list
1419
+ internal.priority = internal.priority - (priority > 0 ? 1 : 0); // We use the render flag and deprecate priority
1420
+
1421
+ if (!internal.priority && state.internal.render === 'manual') set(() => ({
1422
+ internal: { ...state.internal,
1423
+ render: 'auto'
1424
+ }
1425
+ })); // Remove subscriber from list
1397
1426
 
1398
1427
  internal.subscribers = internal.subscribers.filter(s => s.ref !== ref);
1399
1428
  }
@@ -1449,8 +1478,6 @@ function createSubs(callback, subs) {
1449
1478
  subs.add(sub);
1450
1479
  return () => void subs.delete(sub);
1451
1480
  }
1452
-
1453
- let i;
1454
1481
  let globalEffects = new Set();
1455
1482
  let globalAfterEffects = new Set();
1456
1483
  let globalTailEffects = new Set();
@@ -1479,10 +1506,7 @@ function run(effects, timestamp) {
1479
1506
  }) => callback(timestamp));
1480
1507
  }
1481
1508
 
1482
- let subscribers;
1483
- let subscription;
1484
-
1485
- function render$1(timestamp, state, frame) {
1509
+ function update(timestamp, state, frame) {
1486
1510
  // Run local effects
1487
1511
  let delta = state.clock.getDelta(); // In frameloop='never' mode, clock times are updated using the provided timestamp
1488
1512
 
@@ -1490,18 +1514,14 @@ function render$1(timestamp, state, frame) {
1490
1514
  delta = timestamp - state.clock.elapsedTime;
1491
1515
  state.clock.oldTime = state.clock.elapsedTime;
1492
1516
  state.clock.elapsedTime = timestamp;
1493
- } // Call subscribers (useFrame)
1494
-
1495
-
1496
- subscribers = state.internal.subscribers;
1517
+ } else {
1518
+ delta = Math.max(Math.min(delta, state.internal.maxDelta), 0);
1519
+ } // Call subscribers (useUpdate)
1497
1520
 
1498
- for (i = 0; i < subscribers.length; i++) {
1499
- subscription = subscribers[i];
1500
- subscription.ref.current(subscription.store.getState(), delta, frame);
1501
- } // Render content
1502
1521
 
1503
-
1504
- if (!state.internal.priority && state.gl.render) state.gl.render(state.scene, state.camera); // Decrease frame count
1522
+ for (const stage of state.internal.stages) {
1523
+ stage.frame(delta, frame);
1524
+ }
1505
1525
 
1506
1526
  state.internal.frames = Math.max(0, state.internal.frames - 1);
1507
1527
  return state.frameloop === 'always' ? 1 : state.internal.frames;
@@ -1526,7 +1546,7 @@ function createLoop(roots) {
1526
1546
  state = root.store.getState(); // If the frameloop is invalidated, do not run another frame
1527
1547
 
1528
1548
  if (state.internal.active && (state.frameloop === 'always' || state.internal.frames > 0) && !((_state$gl$xr = state.gl.xr) != null && _state$gl$xr.isPresenting)) {
1529
- repeat += render$1(timestamp, state);
1549
+ repeat += update(timestamp, state);
1530
1550
  }
1531
1551
  }); // Run after-effects
1532
1552
 
@@ -1557,7 +1577,7 @@ function createLoop(roots) {
1557
1577
 
1558
1578
  function advance(timestamp, runGlobalEffects = true, state, frame) {
1559
1579
  if (runGlobalEffects) run(globalEffects, timestamp);
1560
- if (!state) roots.forEach(root => render$1(timestamp, root.store.getState()));else render$1(timestamp, state, frame);
1580
+ if (!state) roots.forEach(root => update(timestamp, root.store.getState()));else update(timestamp, state, frame);
1561
1581
  if (runGlobalEffects) run(globalAfterEffects, timestamp);
1562
1582
  }
1563
1583
 
@@ -1578,6 +1598,154 @@ function createLoop(roots) {
1578
1598
  };
1579
1599
  }
1580
1600
 
1601
+ /**
1602
+ * Class representing a stage that updates every frame.
1603
+ * Stages are used to build a lifecycle of effects for an app's frameloop.
1604
+ */
1605
+ class Stage {
1606
+ constructor() {
1607
+ this.subscribers = [];
1608
+ this._frameTime = 0;
1609
+ }
1610
+ /**
1611
+ * Executes all callback subscriptions on the stage.
1612
+ * @param delta - Delta time between frame calls.
1613
+ * @param [frame] - The XR frame if it exists.
1614
+ */
1615
+
1616
+
1617
+ frame(delta, frame) {
1618
+ const subs = this.subscribers;
1619
+ const initialTime = performance.now();
1620
+
1621
+ for (let i = 0; i < subs.length; i++) {
1622
+ subs[i].ref.current(subs[i].store.getState(), delta, frame);
1623
+ }
1624
+
1625
+ this._frameTime = performance.now() - initialTime;
1626
+ }
1627
+ /**
1628
+ * Adds a callback subscriber to the stage.
1629
+ * @param ref - The mutable callback reference.
1630
+ * @param store - The store to be used with the callback execution.
1631
+ * @returns A function to remove the subscription.
1632
+ */
1633
+
1634
+
1635
+ add(ref, store) {
1636
+ this.subscribers.push({
1637
+ ref,
1638
+ store
1639
+ });
1640
+ return () => {
1641
+ this.subscribers = this.subscribers.filter(sub => {
1642
+ return sub.ref !== ref;
1643
+ });
1644
+ };
1645
+ }
1646
+
1647
+ get frameTime() {
1648
+ return this._frameTime;
1649
+ }
1650
+
1651
+ } // Using Unity's fixedStep default.
1652
+
1653
+ const FPS_50 = 1 / 50;
1654
+ /**
1655
+ * Class representing a stage that updates every frame at a fixed rate.
1656
+ * @param name - Name of the stage.
1657
+ * @param [fixedStep] - Fixed step rate.
1658
+ * @param [maxSubsteps] - Maximum number of substeps.
1659
+ */
1660
+
1661
+ class FixedStage extends Stage {
1662
+ constructor(fixedStep, maxSubSteps) {
1663
+ super();
1664
+ this._fixedStep = fixedStep != null ? fixedStep : FPS_50;
1665
+ this._maxSubsteps = maxSubSteps != null ? maxSubSteps : 6;
1666
+ this._accumulator = 0;
1667
+ this._alpha = 0;
1668
+ this._fixedFrameTime = 0;
1669
+ this._substepTimes = [];
1670
+ }
1671
+ /**
1672
+ * Executes all callback subscriptions on the stage.
1673
+ * @param delta - Delta time between frame calls.
1674
+ * @param [frame] - The XR frame if it exists.
1675
+ */
1676
+
1677
+
1678
+ frame(delta, frame) {
1679
+ const initialTime = performance.now();
1680
+ let substeps = 0;
1681
+ this._substepTimes = [];
1682
+ this._accumulator += delta;
1683
+
1684
+ while (this._accumulator >= this._fixedStep && substeps < this._maxSubsteps) {
1685
+ this._accumulator -= this._fixedStep;
1686
+ substeps++;
1687
+ super.frame(this._fixedStep, frame);
1688
+
1689
+ this._substepTimes.push(super.frameTime);
1690
+ }
1691
+
1692
+ this._fixedFrameTime = performance.now() - initialTime; // The accumulator will only be larger than the fixed step if we had to
1693
+ // bail early due to hitting the max substep limit or execution time lagging.
1694
+ // In that case, we want to shave off the excess so we don't fall behind next frame.
1695
+
1696
+ this._accumulator = this._accumulator % this._fixedStep;
1697
+ this._alpha = this._accumulator / this._fixedStep;
1698
+ }
1699
+
1700
+ get frameTime() {
1701
+ return this._fixedFrameTime;
1702
+ }
1703
+
1704
+ get substepTimes() {
1705
+ return this._substepTimes;
1706
+ }
1707
+
1708
+ get fixedStep() {
1709
+ return this._fixedStep;
1710
+ }
1711
+
1712
+ set fixedStep(fixedStep) {
1713
+ this._fixedStep = fixedStep;
1714
+ }
1715
+
1716
+ get maxSubsteps() {
1717
+ return this._maxSubsteps;
1718
+ }
1719
+
1720
+ set maxSubsteps(maxSubsteps) {
1721
+ this._maxSubsteps = maxSubsteps;
1722
+ }
1723
+
1724
+ get accumulator() {
1725
+ return this._accumulator;
1726
+ }
1727
+
1728
+ get alpha() {
1729
+ return this._alpha;
1730
+ }
1731
+
1732
+ }
1733
+ const Early = new Stage();
1734
+ const Fixed = new FixedStage();
1735
+ const Update = new Stage();
1736
+ const Late = new Stage();
1737
+ const Render = new Stage();
1738
+ const After = new Stage();
1739
+ const Stages = {
1740
+ Early,
1741
+ Fixed,
1742
+ Update,
1743
+ Late,
1744
+ Render,
1745
+ After
1746
+ };
1747
+ const Lifecycle = [Early, Fixed, Update, Late, Render, After];
1748
+
1581
1749
  function useStore() {
1582
1750
  const store = React__namespace.useContext(context);
1583
1751
  if (!store) throw `R3F hooks can only be used within the Canvas component!`;
@@ -1606,6 +1774,21 @@ function useFrame(callback, renderPriority = 0) {
1606
1774
  useIsomorphicLayoutEffect(() => subscribe(ref, renderPriority, store), [renderPriority, subscribe, store]);
1607
1775
  return null;
1608
1776
  }
1777
+ /**
1778
+ * Executes a callback in a given update stage.
1779
+ * Uses the stage instance to indetify which stage to target in the lifecycle.
1780
+ */
1781
+
1782
+ function useUpdate(callback, stage = Stages.Update) {
1783
+ const store = useStore();
1784
+ const stages = store.getState().internal.stages; // Memoize ref
1785
+
1786
+ const ref = useMutableCallback(callback); // Throw an error if a stage does not exist in the lifecycle
1787
+
1788
+ if (!stages.includes(stage)) throw new Error(`An invoked stage does not exist in the lifecycle.`); // Subscribe on mount, unsubscribe on unmount
1789
+
1790
+ useIsomorphicLayoutEffect(() => stage.add(ref, store), [stage]);
1791
+ }
1609
1792
  /**
1610
1793
  * Returns a node graph of an object with named nodes & materials.
1611
1794
  * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usegraph
@@ -1687,6 +1870,43 @@ const createRendererInstance = (gl, canvas) => {
1687
1870
  });
1688
1871
  };
1689
1872
 
1873
+ const createStages = (stages, store) => {
1874
+ var _stages;
1875
+
1876
+ const state = store.getState();
1877
+ let subscribers;
1878
+ let subscription;
1879
+ stages = (_stages = stages) != null ? _stages : Lifecycle;
1880
+ if (!stages.includes(Stages.Update)) throw 'The Stages.Update stage is required for R3F.';
1881
+ if (!stages.includes(Stages.Render)) throw 'The Stages.Render stage is required for R3F.';
1882
+ state.set(({
1883
+ internal
1884
+ }) => ({
1885
+ internal: { ...internal,
1886
+ stages: stages
1887
+ }
1888
+ })); // Add useFrame loop to update stage
1889
+
1890
+ const frameCallback = {
1891
+ current: (state, delta, frame) => {
1892
+ subscribers = state.internal.subscribers;
1893
+
1894
+ for (let i = 0; i < subscribers.length; i++) {
1895
+ subscription = subscribers[i];
1896
+ subscription.ref.current(subscription.store.getState(), delta, frame);
1897
+ }
1898
+ }
1899
+ };
1900
+ Stages.Update.add(frameCallback, store); // Add render callback to render stage
1901
+
1902
+ const renderCallback = {
1903
+ current: state => {
1904
+ if (state.internal.render === 'auto' && state.gl.render) state.gl.render(state.scene, state.camera);
1905
+ }
1906
+ };
1907
+ Stages.Render.add(renderCallback, store);
1908
+ };
1909
+
1690
1910
  function createRoot(canvas) {
1691
1911
  // Check against mistaken use of createRoot
1692
1912
  let prevRoot = roots.get(canvas);
@@ -1728,7 +1948,8 @@ function createRoot(canvas) {
1728
1948
  performance,
1729
1949
  raycaster: raycastOptions,
1730
1950
  camera: cameraOptions,
1731
- onPointerMissed
1951
+ onPointerMissed,
1952
+ stages
1732
1953
  } = props;
1733
1954
  let state = store.getState(); // Set up renderer (one time only!)
1734
1955
 
@@ -1819,14 +2040,8 @@ function createRoot(canvas) {
1819
2040
  if (!isBoolean) Object.assign(gl.shadowMap, shadows);else gl.shadowMap.type = THREE__namespace.PCFSoftShadowMap;
1820
2041
  if (old !== gl.shadowMap.enabled) gl.shadowMap.needsUpdate = true;
1821
2042
  }
1822
- } // Safely set color management if available.
1823
- // Avoid accessing THREE.ColorManagement to play nice with older versions
1824
-
1825
-
1826
- if ('ColorManagement' in THREE__namespace) {
1827
- setDeep(THREE__namespace, legacy, ['ColorManagement', 'legacyMode']);
1828
- }
1829
-
2043
+ } // Set color management
2044
+ THREE__namespace.ColorManagement.legacyMode = legacy;
1830
2045
  const outputEncoding = linear ? THREE__namespace.LinearEncoding : THREE__namespace.sRGBEncoding;
1831
2046
  const toneMapping = flat ? THREE__namespace.NoToneMapping : THREE__namespace.ACESFilmicToneMapping;
1832
2047
  if (gl.outputEncoding !== outputEncoding) gl.outputEncoding = outputEncoding;
@@ -1877,7 +2092,9 @@ function createRoot(canvas) {
1877
2092
  performance: { ...state.performance,
1878
2093
  ...performance
1879
2094
  }
1880
- })); // Set locals
2095
+ })); // Create update stages.
2096
+
2097
+ if (stages !== state.internal.stages) createStages(stages, store); // Set locals
1881
2098
 
1882
2099
  onCreated = onCreatedCallback;
1883
2100
  configured = true;
@@ -2091,6 +2308,9 @@ const act = React__namespace.unstable_act;
2091
2308
 
2092
2309
  exports.Block = Block;
2093
2310
  exports.ErrorBoundary = ErrorBoundary;
2311
+ exports.FixedStage = FixedStage;
2312
+ exports.Stage = Stage;
2313
+ exports.Stages = Stages;
2094
2314
  exports.act = act;
2095
2315
  exports.addAfterEffect = addAfterEffect;
2096
2316
  exports.addEffect = addEffect;
@@ -2117,3 +2337,4 @@ exports.useLoader = useLoader;
2117
2337
  exports.useMutableCallback = useMutableCallback;
2118
2338
  exports.useStore = useStore;
2119
2339
  exports.useThree = useThree;
2340
+ exports.useUpdate = useUpdate;