@react-three/fiber 7.0.3 → 7.0.7

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/CHANGELOG.md CHANGED
@@ -1,9 +1,34 @@
1
1
  # @react-three/fiber
2
2
 
3
+ ## 7.0.7
4
+
5
+ ### Patch Changes
6
+
7
+ - 0375896: Simplify useframe, support instanced event cancelation, silence disposal
8
+
9
+ ## 7.0.6
10
+
11
+ ### Patch Changes
12
+
13
+ - fb052ad: Fix babel-env browserslist transpiling into old code"
14
+
15
+ ## 7.0.5
16
+
17
+ ### Patch Changes
18
+
19
+ - c97794a: Add useLoader.clear(Loader, input)
20
+
21
+ ## 7.0.4
22
+
23
+ ### Patch Changes
24
+
25
+ - 974ecfb: Allow elements to define attachFns for specific mount/unmount
26
+
3
27
  ## 7.0.2
4
28
 
5
29
  ### Patch Changes
6
30
 
31
+ - a97aca3: Add controls state field
7
32
  - 4c703d6: fix rttr didn't work with r130
8
33
 
9
34
  ## 7.0.0
@@ -17,11 +17,13 @@ export declare type ObjectMap = {
17
17
  [name: string]: THREE.Material;
18
18
  };
19
19
  };
20
+ export declare function useStore(): import("zustand").UseStore<RootState>;
20
21
  export declare function useThree<T = RootState>(selector?: StateSelector<RootState, T>, equalityFn?: EqualityChecker<T>): T;
21
22
  export declare function useFrame(callback: RenderCallback, renderPriority?: number): null;
22
23
  export declare function useGraph(object: THREE.Object3D): ObjectMap;
23
24
  export declare function useLoader<T, U extends string | string[]>(Proto: new () => LoaderResult<T>, input: U, extensions?: Extensions, onProgress?: (event: ProgressEvent<EventTarget>) => void): U extends any[] ? BranchingReturn<T, GLTF, GLTF & ObjectMap>[] : BranchingReturn<T, GLTF, GLTF & ObjectMap>;
24
25
  export declare namespace useLoader {
25
26
  var preload: <T, U extends string | string[]>(Proto: new () => LoaderResult<T>, input: U, extensions?: Extensions | undefined) => undefined;
27
+ var clear: <T, U extends string | string[]>(Proto: new () => LoaderResult<T>, input: U) => void;
26
28
  }
27
29
  export {};
@@ -1,8 +1,8 @@
1
1
  export declare const is: {
2
2
  obj: (a: any) => boolean;
3
- fun: (a: any) => boolean;
4
- str: (a: any) => boolean;
5
- num: (a: any) => boolean;
3
+ fun: (a: any) => a is Function;
4
+ str: (a: any) => a is string;
5
+ num: (a: any) => a is number;
6
6
  und: (a: any) => boolean;
7
7
  arr: (a: any) => boolean;
8
8
  equ(a: any, b: any): boolean;
@@ -19,11 +19,14 @@ export declare type LocalState = {
19
19
  export declare type ClassConstructor = {
20
20
  new (): void;
21
21
  };
22
+ export declare type AttachFnType = (self: Instance, parent: Instance) => void;
23
+ export declare type AttachFnsType = [attach: string | AttachFnType, detach: string | AttachFnType];
22
24
  export declare type BaseInstance = Omit<THREE.Object3D, 'parent' | 'children' | 'attach' | 'add' | 'remove' | 'raycast'> & {
23
25
  __r3f: LocalState;
24
26
  parent: Instance | null;
25
27
  children: Instance[];
26
28
  attach?: string;
29
+ attachFns?: AttachFnsType;
27
30
  remove: (...object: Instance[]) => Instance;
28
31
  add: (...object: Instance[]) => Instance;
29
32
  raycast?: (raycaster: THREE.Raycaster, intersects: THREE.Intersection[]) => void;
@@ -56,6 +56,7 @@ export declare type RootState = {
56
56
  gl: THREE.WebGLRenderer;
57
57
  scene: THREE.Scene;
58
58
  camera: Camera;
59
+ controls: THREE.EventDispatcher | null;
59
60
  raycaster: Raycaster;
60
61
  mouse: THREE.Vector2;
61
62
  clock: THREE.Clock;
@@ -14,10 +14,12 @@ export declare type Color = ConstructorParameters<typeof THREE.Color> | THREE.Co
14
14
  export declare type ColorArray = typeof THREE.Color | Parameters<THREE.Color['set']>;
15
15
  export declare type Layers = THREE.Layers | Parameters<THREE.Layers['set']>[0];
16
16
  export declare type Quaternion = THREE.Quaternion | Parameters<THREE.Quaternion['set']>;
17
+ export declare type AttachCallback = string | ((child: any, parentInstance: any) => void);
17
18
  export interface NodeProps<T, P> {
18
19
  attach?: string;
19
20
  attachArray?: string;
20
21
  attachObject?: [target: string, name: string];
22
+ attachFns?: [AttachCallback, AttachCallback];
21
23
  args?: Args<P>;
22
24
  children?: React.ReactNode;
23
25
  ref?: React.Ref<React.ReactNode>;
@@ -13,7 +13,7 @@ declare const modes: readonly ["legacy", "blocking", "concurrent"];
13
13
  declare const invalidate: (state?: RootState | undefined) => void, advance: (timestamp: number, runGlobalEffects?: boolean, state?: RootState | undefined) => void;
14
14
  declare const reconciler: import("react-reconciler").Reconciler<unknown, unknown, unknown, unknown, unknown>, applyProps: (instance: import("../core/renderer").Instance, newProps: import("../core/renderer").InstanceProps, oldProps?: import("../core/renderer").InstanceProps, accumulative?: boolean) => void;
15
15
  export declare type RenderProps<TCanvas extends Element> = Omit<StoreProps, 'gl' | 'events' | 'size'> & {
16
- gl?: THREE.WebGLRenderer | THREE.WebGLRendererParameters;
16
+ gl?: THREE.WebGLRenderer | Partial<THREE.WebGLRendererParameters>;
17
17
  events?: (store: UseStore<RootState>) => EventManager<TCanvas>;
18
18
  size?: Size;
19
19
  mode?: typeof modes[number];
@@ -74,7 +74,7 @@ const is = {
74
74
  };
75
75
 
76
76
  function makeId(event) {
77
- return (event.eventObject || event.object).uuid + '/' + event.index;
77
+ return (event.eventObject || event.object).uuid + '/' + event.index + event.instanceId;
78
78
  }
79
79
 
80
80
  function removeInteractivity(store, object) {
@@ -306,7 +306,7 @@ function createEvents(store) {
306
306
  Array.from(internal.hovered.values()).forEach(hoveredObj => {
307
307
  // When no objects were hit or the the hovered object wasn't found underneath the cursor
308
308
  // we call onPointerOut and delete the object from the hovered-elements map
309
- if (!hits.length || !hits.find(hit => hit.object === hoveredObj.object && hit.index === hoveredObj.index)) {
309
+ if (!hits.length || !hits.find(hit => hit.object === hoveredObj.object && hit.index === hoveredObj.index && hit.instanceId === hoveredObj.instanceId)) {
310
310
  const eventObject = hoveredObj.eventObject;
311
311
  const handlers = eventObject.__r3f.handlers;
312
312
  internal.hovered.delete(makeId(hoveredObj));
@@ -704,16 +704,18 @@ function createRenderer(roots) {
704
704
  } // Auto-attach geometries and materials
705
705
 
706
706
 
707
- if (name.endsWith('Geometry')) {
708
- props = {
709
- attach: 'geometry',
710
- ...props
711
- };
712
- } else if (name.endsWith('Material')) {
713
- props = {
714
- attach: 'material',
715
- ...props
716
- };
707
+ if (!('attachFns' in props)) {
708
+ if (name.endsWith('Geometry')) {
709
+ props = {
710
+ attach: 'geometry',
711
+ ...props
712
+ };
713
+ } else if (name.endsWith('Material')) {
714
+ props = {
715
+ attach: 'material',
716
+ ...props
717
+ };
718
+ }
717
719
  } // It should NOT call onUpdate on object instanciation, because it hasn't been added to the
718
720
  // view yet. If the callback relies on references for instance, they won't be ready yet, this is
719
721
  // why it passes "true" here
@@ -736,6 +738,14 @@ function createRenderer(roots) {
736
738
  parentInstance[child.attachObject[0]][child.attachObject[1]] = child;
737
739
  } else if (child.attach && !is.fun(child.attach)) {
738
740
  parentInstance[child.attach] = child;
741
+ } else if (is.arr(child.attachFns)) {
742
+ const [attachFn] = child.attachFns;
743
+
744
+ if (is.str(attachFn) && is.fun(parentInstance[attachFn])) {
745
+ parentInstance[attachFn](child);
746
+ } else if (is.fun(attachFn)) {
747
+ attachFn(child, parentInstance);
748
+ }
739
749
  } else if (child.isObject3D) {
740
750
  // add in the usual parent-child way
741
751
  parentInstance.add(child);
@@ -814,6 +824,14 @@ function createRenderer(roots) {
814
824
  delete parentInstance[child.attachObject[0]][child.attachObject[1]];
815
825
  } else if (child.attach && !is.fun(child.attach)) {
816
826
  parentInstance[child.attach] = null;
827
+ } else if (is.arr(child.attachFns)) {
828
+ const [, detachFn] = child.attachFns;
829
+
830
+ if (is.str(detachFn) && is.fun(parentInstance[detachFn])) {
831
+ parentInstance[detachFn](child);
832
+ } else if (is.fun(detachFn)) {
833
+ detachFn(child, parentInstance);
834
+ }
817
835
  } else if (child.isObject3D) {
818
836
  var _child$__r3f;
819
837
 
@@ -826,7 +844,7 @@ function createRenderer(roots) {
826
844
  // Never dispose of primitives because their state may be kept outside of React!
827
845
  // In order for an object to be able to dispose it has to have
828
846
  // - a dispose method,
829
- // - it cannot be an <instance object={...} />
847
+ // - it cannot be a <primitive object={...} />
830
848
  // - it cannot be a THREE.Scene, because three has broken it's own api
831
849
  //
832
850
  // Since disposal is recursive, we can check the optional dispose arg, which will be undefined
@@ -855,7 +873,13 @@ function createRenderer(roots) {
855
873
 
856
874
 
857
875
  if (shouldDispose && child.dispose && child.type !== 'Scene') {
858
- scheduler.unstable_runWithPriority(scheduler.unstable_IdlePriority, () => child.dispose());
876
+ scheduler.unstable_runWithPriority(scheduler.unstable_IdlePriority, () => {
877
+ try {
878
+ child.dispose();
879
+ } catch (e) {
880
+ /* ... */
881
+ }
882
+ });
859
883
  }
860
884
 
861
885
  invalidateInstance(parentInstance);
@@ -1163,6 +1187,7 @@ const createStore = (applyProps, invalidate, advance, props) => {
1163
1187
  flat,
1164
1188
  scene: prepare(new THREE__namespace.Scene()),
1165
1189
  camera,
1190
+ controls: null,
1166
1191
  raycaster,
1167
1192
  clock,
1168
1193
  mouse: new THREE__namespace.Vector2(),
@@ -1237,8 +1262,8 @@ const createStore = (applyProps, invalidate, advance, props) => {
1237
1262
  internal: { ...internal,
1238
1263
  // If this subscription was given a priority, it takes rendering into its own hands
1239
1264
  // For that reason we switch off automatic rendering and increase the manual flag
1240
- // As long as this flag is positive (there could be multiple render subscription)
1241
- // ..there can be no internal rendering at all
1265
+ // As long as this flag is positive there can be no internal rendering at all
1266
+ // because there could be multiple render subscriptions
1242
1267
  priority: internal.priority + (priority > 0 ? 1 : 0),
1243
1268
  // Register subscriber and sort layers from lowest to highest, meaning,
1244
1269
  // highest priority renders last (on top of the other frames)
@@ -1558,23 +1583,21 @@ const Canvas = /*#__PURE__*/React__namespace.forwardRef(function Canvas({
1558
1583
  }, fallback));
1559
1584
  });
1560
1585
 
1586
+ function useStore() {
1587
+ const store = React__namespace.useContext(context);
1588
+ if (!store) throw `R3F hooks can only be used within the Canvas component!`;
1589
+ return store;
1590
+ }
1561
1591
  function useThree(selector = state => state, equalityFn) {
1562
- const useStore = React__namespace.useContext(context);
1563
- if (!useStore) throw `R3F hooks can only be used within the Canvas component!`;
1564
- return useStore(selector, equalityFn);
1592
+ return useStore()(selector, equalityFn);
1565
1593
  }
1566
1594
  function useFrame(callback, renderPriority = 0) {
1567
- const {
1568
- subscribe
1569
- } = React__namespace.useContext(context).getState().internal; // Update ref
1595
+ const subscribe = useStore().getState().internal.subscribe; // Update ref
1570
1596
 
1571
1597
  const ref = React__namespace.useRef(callback);
1572
- React__namespace.useLayoutEffect(() => void (ref.current = callback), [callback]); // Subscribe/unsub
1598
+ React__namespace.useLayoutEffect(() => void (ref.current = callback), [callback]); // Subscribe on mount, unsubscribe on unmount
1573
1599
 
1574
- React__namespace.useLayoutEffect(() => {
1575
- const unsubscribe = subscribe(ref, renderPriority);
1576
- return () => unsubscribe();
1577
- }, [renderPriority, subscribe]);
1600
+ React__namespace.useLayoutEffect(() => subscribe(ref, renderPriority), [renderPriority]);
1578
1601
  return null;
1579
1602
  }
1580
1603
 
@@ -1629,6 +1652,11 @@ useLoader.preload = function (Proto, input, extensions) {
1629
1652
  return useAsset.useAsset.preload(loadingFn(extensions), Proto, ...keys);
1630
1653
  };
1631
1654
 
1655
+ useLoader.clear = function (Proto, input) {
1656
+ const keys = Array.isArray(input) ? input : [input];
1657
+ return useAsset.useAsset.clear(Proto, ...keys);
1658
+ };
1659
+
1632
1660
  const roots = new Map();
1633
1661
  const modes = ['legacy', 'blocking', 'concurrent'];
1634
1662
  const {
@@ -1832,4 +1860,5 @@ exports.unmountComponentAtNode = unmountComponentAtNode;
1832
1860
  exports.useFrame = useFrame;
1833
1861
  exports.useGraph = useGraph;
1834
1862
  exports.useLoader = useLoader;
1863
+ exports.useStore = useStore;
1835
1864
  exports.useThree = useThree;
@@ -74,7 +74,7 @@ const is = {
74
74
  };
75
75
 
76
76
  function makeId(event) {
77
- return (event.eventObject || event.object).uuid + '/' + event.index;
77
+ return (event.eventObject || event.object).uuid + '/' + event.index + event.instanceId;
78
78
  }
79
79
 
80
80
  function removeInteractivity(store, object) {
@@ -306,7 +306,7 @@ function createEvents(store) {
306
306
  Array.from(internal.hovered.values()).forEach(hoveredObj => {
307
307
  // When no objects were hit or the the hovered object wasn't found underneath the cursor
308
308
  // we call onPointerOut and delete the object from the hovered-elements map
309
- if (!hits.length || !hits.find(hit => hit.object === hoveredObj.object && hit.index === hoveredObj.index)) {
309
+ if (!hits.length || !hits.find(hit => hit.object === hoveredObj.object && hit.index === hoveredObj.index && hit.instanceId === hoveredObj.instanceId)) {
310
310
  const eventObject = hoveredObj.eventObject;
311
311
  const handlers = eventObject.__r3f.handlers;
312
312
  internal.hovered.delete(makeId(hoveredObj));
@@ -704,16 +704,18 @@ function createRenderer(roots) {
704
704
  } // Auto-attach geometries and materials
705
705
 
706
706
 
707
- if (name.endsWith('Geometry')) {
708
- props = {
709
- attach: 'geometry',
710
- ...props
711
- };
712
- } else if (name.endsWith('Material')) {
713
- props = {
714
- attach: 'material',
715
- ...props
716
- };
707
+ if (!('attachFns' in props)) {
708
+ if (name.endsWith('Geometry')) {
709
+ props = {
710
+ attach: 'geometry',
711
+ ...props
712
+ };
713
+ } else if (name.endsWith('Material')) {
714
+ props = {
715
+ attach: 'material',
716
+ ...props
717
+ };
718
+ }
717
719
  } // It should NOT call onUpdate on object instanciation, because it hasn't been added to the
718
720
  // view yet. If the callback relies on references for instance, they won't be ready yet, this is
719
721
  // why it passes "true" here
@@ -736,6 +738,14 @@ function createRenderer(roots) {
736
738
  parentInstance[child.attachObject[0]][child.attachObject[1]] = child;
737
739
  } else if (child.attach && !is.fun(child.attach)) {
738
740
  parentInstance[child.attach] = child;
741
+ } else if (is.arr(child.attachFns)) {
742
+ const [attachFn] = child.attachFns;
743
+
744
+ if (is.str(attachFn) && is.fun(parentInstance[attachFn])) {
745
+ parentInstance[attachFn](child);
746
+ } else if (is.fun(attachFn)) {
747
+ attachFn(child, parentInstance);
748
+ }
739
749
  } else if (child.isObject3D) {
740
750
  // add in the usual parent-child way
741
751
  parentInstance.add(child);
@@ -814,6 +824,14 @@ function createRenderer(roots) {
814
824
  delete parentInstance[child.attachObject[0]][child.attachObject[1]];
815
825
  } else if (child.attach && !is.fun(child.attach)) {
816
826
  parentInstance[child.attach] = null;
827
+ } else if (is.arr(child.attachFns)) {
828
+ const [, detachFn] = child.attachFns;
829
+
830
+ if (is.str(detachFn) && is.fun(parentInstance[detachFn])) {
831
+ parentInstance[detachFn](child);
832
+ } else if (is.fun(detachFn)) {
833
+ detachFn(child, parentInstance);
834
+ }
817
835
  } else if (child.isObject3D) {
818
836
  var _child$__r3f;
819
837
 
@@ -826,7 +844,7 @@ function createRenderer(roots) {
826
844
  // Never dispose of primitives because their state may be kept outside of React!
827
845
  // In order for an object to be able to dispose it has to have
828
846
  // - a dispose method,
829
- // - it cannot be an <instance object={...} />
847
+ // - it cannot be a <primitive object={...} />
830
848
  // - it cannot be a THREE.Scene, because three has broken it's own api
831
849
  //
832
850
  // Since disposal is recursive, we can check the optional dispose arg, which will be undefined
@@ -855,7 +873,13 @@ function createRenderer(roots) {
855
873
 
856
874
 
857
875
  if (shouldDispose && child.dispose && child.type !== 'Scene') {
858
- scheduler.unstable_runWithPriority(scheduler.unstable_IdlePriority, () => child.dispose());
876
+ scheduler.unstable_runWithPriority(scheduler.unstable_IdlePriority, () => {
877
+ try {
878
+ child.dispose();
879
+ } catch (e) {
880
+ /* ... */
881
+ }
882
+ });
859
883
  }
860
884
 
861
885
  invalidateInstance(parentInstance);
@@ -1163,6 +1187,7 @@ const createStore = (applyProps, invalidate, advance, props) => {
1163
1187
  flat,
1164
1188
  scene: prepare(new THREE__namespace.Scene()),
1165
1189
  camera,
1190
+ controls: null,
1166
1191
  raycaster,
1167
1192
  clock,
1168
1193
  mouse: new THREE__namespace.Vector2(),
@@ -1237,8 +1262,8 @@ const createStore = (applyProps, invalidate, advance, props) => {
1237
1262
  internal: { ...internal,
1238
1263
  // If this subscription was given a priority, it takes rendering into its own hands
1239
1264
  // For that reason we switch off automatic rendering and increase the manual flag
1240
- // As long as this flag is positive (there could be multiple render subscription)
1241
- // ..there can be no internal rendering at all
1265
+ // As long as this flag is positive there can be no internal rendering at all
1266
+ // because there could be multiple render subscriptions
1242
1267
  priority: internal.priority + (priority > 0 ? 1 : 0),
1243
1268
  // Register subscriber and sort layers from lowest to highest, meaning,
1244
1269
  // highest priority renders last (on top of the other frames)
@@ -1558,23 +1583,21 @@ const Canvas = /*#__PURE__*/React__namespace.forwardRef(function Canvas({
1558
1583
  }, fallback));
1559
1584
  });
1560
1585
 
1586
+ function useStore() {
1587
+ const store = React__namespace.useContext(context);
1588
+ if (!store) throw `R3F hooks can only be used within the Canvas component!`;
1589
+ return store;
1590
+ }
1561
1591
  function useThree(selector = state => state, equalityFn) {
1562
- const useStore = React__namespace.useContext(context);
1563
- if (!useStore) throw `R3F hooks can only be used within the Canvas component!`;
1564
- return useStore(selector, equalityFn);
1592
+ return useStore()(selector, equalityFn);
1565
1593
  }
1566
1594
  function useFrame(callback, renderPriority = 0) {
1567
- const {
1568
- subscribe
1569
- } = React__namespace.useContext(context).getState().internal; // Update ref
1595
+ const subscribe = useStore().getState().internal.subscribe; // Update ref
1570
1596
 
1571
1597
  const ref = React__namespace.useRef(callback);
1572
- React__namespace.useLayoutEffect(() => void (ref.current = callback), [callback]); // Subscribe/unsub
1598
+ React__namespace.useLayoutEffect(() => void (ref.current = callback), [callback]); // Subscribe on mount, unsubscribe on unmount
1573
1599
 
1574
- React__namespace.useLayoutEffect(() => {
1575
- const unsubscribe = subscribe(ref, renderPriority);
1576
- return () => unsubscribe();
1577
- }, [renderPriority, subscribe]);
1600
+ React__namespace.useLayoutEffect(() => subscribe(ref, renderPriority), [renderPriority]);
1578
1601
  return null;
1579
1602
  }
1580
1603
 
@@ -1629,6 +1652,11 @@ useLoader.preload = function (Proto, input, extensions) {
1629
1652
  return useAsset.useAsset.preload(loadingFn(extensions), Proto, ...keys);
1630
1653
  };
1631
1654
 
1655
+ useLoader.clear = function (Proto, input) {
1656
+ const keys = Array.isArray(input) ? input : [input];
1657
+ return useAsset.useAsset.clear(Proto, ...keys);
1658
+ };
1659
+
1632
1660
  const roots = new Map();
1633
1661
  const modes = ['legacy', 'blocking', 'concurrent'];
1634
1662
  const {
@@ -1832,4 +1860,5 @@ exports.unmountComponentAtNode = unmountComponentAtNode;
1832
1860
  exports.useFrame = useFrame;
1833
1861
  exports.useGraph = useGraph;
1834
1862
  exports.useLoader = useLoader;
1863
+ exports.useStore = useStore;
1835
1864
  exports.useThree = useThree;
@@ -40,7 +40,7 @@ const is = {
40
40
  };
41
41
 
42
42
  function makeId(event) {
43
- return (event.eventObject || event.object).uuid + '/' + event.index;
43
+ return (event.eventObject || event.object).uuid + '/' + event.index + event.instanceId;
44
44
  }
45
45
 
46
46
  function removeInteractivity(store, object) {
@@ -272,7 +272,7 @@ function createEvents(store) {
272
272
  Array.from(internal.hovered.values()).forEach(hoveredObj => {
273
273
  // When no objects were hit or the the hovered object wasn't found underneath the cursor
274
274
  // we call onPointerOut and delete the object from the hovered-elements map
275
- if (!hits.length || !hits.find(hit => hit.object === hoveredObj.object && hit.index === hoveredObj.index)) {
275
+ if (!hits.length || !hits.find(hit => hit.object === hoveredObj.object && hit.index === hoveredObj.index && hit.instanceId === hoveredObj.instanceId)) {
276
276
  const eventObject = hoveredObj.eventObject;
277
277
  const handlers = eventObject.__r3f.handlers;
278
278
  internal.hovered.delete(makeId(hoveredObj));
@@ -670,16 +670,18 @@ function createRenderer(roots) {
670
670
  } // Auto-attach geometries and materials
671
671
 
672
672
 
673
- if (name.endsWith('Geometry')) {
674
- props = {
675
- attach: 'geometry',
676
- ...props
677
- };
678
- } else if (name.endsWith('Material')) {
679
- props = {
680
- attach: 'material',
681
- ...props
682
- };
673
+ if (!('attachFns' in props)) {
674
+ if (name.endsWith('Geometry')) {
675
+ props = {
676
+ attach: 'geometry',
677
+ ...props
678
+ };
679
+ } else if (name.endsWith('Material')) {
680
+ props = {
681
+ attach: 'material',
682
+ ...props
683
+ };
684
+ }
683
685
  } // It should NOT call onUpdate on object instanciation, because it hasn't been added to the
684
686
  // view yet. If the callback relies on references for instance, they won't be ready yet, this is
685
687
  // why it passes "true" here
@@ -702,6 +704,14 @@ function createRenderer(roots) {
702
704
  parentInstance[child.attachObject[0]][child.attachObject[1]] = child;
703
705
  } else if (child.attach && !is.fun(child.attach)) {
704
706
  parentInstance[child.attach] = child;
707
+ } else if (is.arr(child.attachFns)) {
708
+ const [attachFn] = child.attachFns;
709
+
710
+ if (is.str(attachFn) && is.fun(parentInstance[attachFn])) {
711
+ parentInstance[attachFn](child);
712
+ } else if (is.fun(attachFn)) {
713
+ attachFn(child, parentInstance);
714
+ }
705
715
  } else if (child.isObject3D) {
706
716
  // add in the usual parent-child way
707
717
  parentInstance.add(child);
@@ -780,6 +790,14 @@ function createRenderer(roots) {
780
790
  delete parentInstance[child.attachObject[0]][child.attachObject[1]];
781
791
  } else if (child.attach && !is.fun(child.attach)) {
782
792
  parentInstance[child.attach] = null;
793
+ } else if (is.arr(child.attachFns)) {
794
+ const [, detachFn] = child.attachFns;
795
+
796
+ if (is.str(detachFn) && is.fun(parentInstance[detachFn])) {
797
+ parentInstance[detachFn](child);
798
+ } else if (is.fun(detachFn)) {
799
+ detachFn(child, parentInstance);
800
+ }
783
801
  } else if (child.isObject3D) {
784
802
  var _child$__r3f;
785
803
 
@@ -792,7 +810,7 @@ function createRenderer(roots) {
792
810
  // Never dispose of primitives because their state may be kept outside of React!
793
811
  // In order for an object to be able to dispose it has to have
794
812
  // - a dispose method,
795
- // - it cannot be an <instance object={...} />
813
+ // - it cannot be a <primitive object={...} />
796
814
  // - it cannot be a THREE.Scene, because three has broken it's own api
797
815
  //
798
816
  // Since disposal is recursive, we can check the optional dispose arg, which will be undefined
@@ -821,7 +839,13 @@ function createRenderer(roots) {
821
839
 
822
840
 
823
841
  if (shouldDispose && child.dispose && child.type !== 'Scene') {
824
- unstable_runWithPriority(unstable_IdlePriority, () => child.dispose());
842
+ unstable_runWithPriority(unstable_IdlePriority, () => {
843
+ try {
844
+ child.dispose();
845
+ } catch (e) {
846
+ /* ... */
847
+ }
848
+ });
825
849
  }
826
850
 
827
851
  invalidateInstance(parentInstance);
@@ -1129,6 +1153,7 @@ const createStore = (applyProps, invalidate, advance, props) => {
1129
1153
  flat,
1130
1154
  scene: prepare(new THREE.Scene()),
1131
1155
  camera,
1156
+ controls: null,
1132
1157
  raycaster,
1133
1158
  clock,
1134
1159
  mouse: new THREE.Vector2(),
@@ -1203,8 +1228,8 @@ const createStore = (applyProps, invalidate, advance, props) => {
1203
1228
  internal: { ...internal,
1204
1229
  // If this subscription was given a priority, it takes rendering into its own hands
1205
1230
  // For that reason we switch off automatic rendering and increase the manual flag
1206
- // As long as this flag is positive (there could be multiple render subscription)
1207
- // ..there can be no internal rendering at all
1231
+ // As long as this flag is positive there can be no internal rendering at all
1232
+ // because there could be multiple render subscriptions
1208
1233
  priority: internal.priority + (priority > 0 ? 1 : 0),
1209
1234
  // Register subscriber and sort layers from lowest to highest, meaning,
1210
1235
  // highest priority renders last (on top of the other frames)
@@ -1524,23 +1549,21 @@ const Canvas = /*#__PURE__*/React.forwardRef(function Canvas({
1524
1549
  }, fallback));
1525
1550
  });
1526
1551
 
1552
+ function useStore() {
1553
+ const store = React.useContext(context);
1554
+ if (!store) throw `R3F hooks can only be used within the Canvas component!`;
1555
+ return store;
1556
+ }
1527
1557
  function useThree(selector = state => state, equalityFn) {
1528
- const useStore = React.useContext(context);
1529
- if (!useStore) throw `R3F hooks can only be used within the Canvas component!`;
1530
- return useStore(selector, equalityFn);
1558
+ return useStore()(selector, equalityFn);
1531
1559
  }
1532
1560
  function useFrame(callback, renderPriority = 0) {
1533
- const {
1534
- subscribe
1535
- } = React.useContext(context).getState().internal; // Update ref
1561
+ const subscribe = useStore().getState().internal.subscribe; // Update ref
1536
1562
 
1537
1563
  const ref = React.useRef(callback);
1538
- React.useLayoutEffect(() => void (ref.current = callback), [callback]); // Subscribe/unsub
1564
+ React.useLayoutEffect(() => void (ref.current = callback), [callback]); // Subscribe on mount, unsubscribe on unmount
1539
1565
 
1540
- React.useLayoutEffect(() => {
1541
- const unsubscribe = subscribe(ref, renderPriority);
1542
- return () => unsubscribe();
1543
- }, [renderPriority, subscribe]);
1566
+ React.useLayoutEffect(() => subscribe(ref, renderPriority), [renderPriority]);
1544
1567
  return null;
1545
1568
  }
1546
1569
 
@@ -1595,6 +1618,11 @@ useLoader.preload = function (Proto, input, extensions) {
1595
1618
  return useAsset.preload(loadingFn(extensions), Proto, ...keys);
1596
1619
  };
1597
1620
 
1621
+ useLoader.clear = function (Proto, input) {
1622
+ const keys = Array.isArray(input) ? input : [input];
1623
+ return useAsset.clear(Proto, ...keys);
1624
+ };
1625
+
1598
1626
  const roots = new Map();
1599
1627
  const modes = ['legacy', 'blocking', 'concurrent'];
1600
1628
  const {
@@ -1777,4 +1805,4 @@ reconciler.injectIntoDevTools({
1777
1805
  version: '17.0.2'
1778
1806
  });
1779
1807
 
1780
- export { Canvas, threeTypes as ReactThreeFiber, roots as _roots, act, addAfterEffect, addEffect, addTail, advance, applyProps, context, createPortal, dispose, createPointerEvents as events, extend, invalidate, reconciler, render, unmountComponentAtNode, useFrame, useGraph, useLoader, useThree };
1808
+ export { Canvas, threeTypes as ReactThreeFiber, roots as _roots, act, addAfterEffect, addEffect, addTail, advance, applyProps, context, createPortal, dispose, createPointerEvents as events, extend, invalidate, reconciler, render, unmountComponentAtNode, useFrame, useGraph, useLoader, useStore, useThree };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-three/fiber",
3
- "version": "7.0.3",
3
+ "version": "7.0.7",
4
4
  "description": "A React renderer for Threejs",
5
5
  "keywords": [
6
6
  "react",