@react-three/fiber 8.0.9 → 8.0.10

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,5 +1,11 @@
1
1
  # @react-three/fiber
2
2
 
3
+ ## 8.0.10
4
+
5
+ ### Patch Changes
6
+
7
+ - eb321afd: fix: remount bug, allow portals to inject custom size
8
+
3
9
  ## 8.0.9
4
10
 
5
11
  ### Patch Changes
@@ -3,11 +3,11 @@ import * as THREE from 'three';
3
3
  import * as React from 'react';
4
4
  import { UseBoundStore } from 'zustand';
5
5
  import * as ReactThreeFiber from '../three-types';
6
- import { Renderer, context, RootState, Size, Camera, Dpr, Performance } from './store';
6
+ import { Renderer, context, RootState, Size, Dpr, Performance } from './store';
7
7
  import { extend, Root } from './renderer';
8
8
  import { addEffect, addAfterEffect, addTail } from './loop';
9
9
  import { EventManager, ComputeFunction } from './events';
10
- import { dispose, getRootState } from './utils';
10
+ import { dispose, getRootState, Camera } from './utils';
11
11
  declare const roots: Map<Element, Root>;
12
12
  declare const invalidate: (state?: RootState | undefined) => void, advance: (timestamp: number, runGlobalEffects?: boolean, state?: RootState | undefined, frame?: THREE.XRFrame | undefined) => void;
13
13
  declare const reconciler: import("react-reconciler").Reconciler<unknown, unknown, unknown, unknown, unknown>, applyProps: typeof import("./utils").applyProps;
@@ -42,13 +42,17 @@ export declare type ReconcilerRoot<TCanvas extends Element> = {
42
42
  declare function createRoot<TCanvas extends Element>(canvas: TCanvas): ReconcilerRoot<TCanvas>;
43
43
  declare function render<TCanvas extends Element>(children: React.ReactNode, canvas: TCanvas, config: RenderProps<TCanvas>): UseBoundStore<RootState>;
44
44
  declare function unmountComponentAtNode<TElement extends Element>(canvas: TElement, callback?: (canvas: TElement) => void): void;
45
- export declare type InjectState = Partial<Omit<RootState, 'set' | 'get' | 'setSize' | 'setFrameloop' | 'setDpr' | 'events' | 'invalidate' | 'advance' | 'performance' | 'internal'> & {
46
- events: {
45
+ export declare type InjectState = Partial<Omit<RootState, 'set' | 'get' | 'setSize' | 'setFrameloop' | 'setDpr' | 'events' | 'invalidate' | 'advance' | 'performance' | 'internal' | 'size' | 'viewport'> & {
46
+ events?: {
47
47
  enabled?: boolean;
48
48
  priority?: number;
49
49
  compute?: ComputeFunction;
50
50
  connected?: any;
51
51
  };
52
+ size?: {
53
+ width: number;
54
+ height: number;
55
+ };
52
56
  }>;
53
57
  declare function createPortal(children: React.ReactNode, container: THREE.Object3D, state?: InjectState): React.ReactNode;
54
58
  declare const act: any;
@@ -2,6 +2,7 @@ import * as THREE from 'three';
2
2
  import * as React from 'react';
3
3
  import { GetState, SetState, StoreApi, UseBoundStore } from 'zustand';
4
4
  import { DomEvent, EventManager, PointerCaptureTarget, ThreeEvent } from './events';
5
+ import { Camera } from './utils';
5
6
  export interface Intersection extends THREE.Intersection {
6
7
  eventObject: THREE.Object3D;
7
8
  }
@@ -22,7 +23,6 @@ export declare type Viewport = Size & {
22
23
  distance: number;
23
24
  aspect: number;
24
25
  };
25
- export declare type Camera = THREE.OrthographicCamera | THREE.PerspectiveCamera;
26
26
  export declare type RenderCallback = (state: RootState, delta: number, frame?: THREE.XRFrame) => void;
27
27
  export declare type Performance = {
28
28
  current: number;
@@ -35,7 +35,6 @@ export declare type Renderer = {
35
35
  render: (scene: THREE.Scene, camera: THREE.Camera) => any;
36
36
  };
37
37
  export declare const isRenderer: (def: any) => boolean;
38
- export declare const isOrthographicCamera: (def: any) => def is THREE.OrthographicCamera;
39
38
  export declare type InternalState = {
40
39
  active: boolean;
41
40
  priority: number;
@@ -1,7 +1,9 @@
1
1
  import * as THREE from 'three';
2
2
  import * as React from 'react';
3
3
  import { AttachType, Instance, InstanceProps, LocalState } from './renderer';
4
- import { Dpr, RootState } from './store';
4
+ import { Dpr, RootState, Size } from './store';
5
+ export declare type Camera = THREE.OrthographicCamera | THREE.PerspectiveCamera;
6
+ export declare const isOrthographicCamera: (def: Camera) => def is THREE.OrthographicCamera;
5
7
  export declare const useIsomorphicLayoutEffect: typeof React.useEffect;
6
8
  export declare function useMutableCallback<T>(fn: T): React.MutableRefObject<T>;
7
9
  export declare type SetBlock = false | Promise<null> | null;
@@ -78,3 +80,6 @@ export declare function diffProps(instance: Instance, { children: cN, key: kN, r
78
80
  export declare function applyProps(instance: Instance, data: InstanceProps | DiffSet): Instance;
79
81
  export declare function invalidateInstance(instance: Instance): void;
80
82
  export declare function updateInstance(instance: Instance): void;
83
+ export declare function updateCamera(camera: Camera & {
84
+ manual?: boolean;
85
+ }, size: Size): void;
@@ -2,9 +2,9 @@ export * from './three-types';
2
2
  import * as ReactThreeFiber from './three-types';
3
3
  export { ReactThreeFiber };
4
4
  export type { BaseInstance, LocalState } from './core/renderer';
5
- export type { Intersection, Subscription, Dpr, Size, Viewport, Camera, RenderCallback, Performance, RootState, } from './core/store';
6
- export type { ThreeEvent, Events, EventManager } from './core/events';
7
- export type { ObjectMap } from './core/utils';
5
+ export type { Intersection, Subscription, Dpr, Size, Viewport, RenderCallback, Performance, RootState, } from './core/store';
6
+ export type { ThreeEvent, Events, EventManager, ComputeFunction } from './core/events';
7
+ export type { ObjectMap, Camera } from './core/utils';
8
8
  export * from './web/Canvas';
9
9
  export { createPointerEvents as events } from './web/events';
10
10
  export * from './core';
@@ -2,9 +2,9 @@ export * from './three-types';
2
2
  import * as ReactThreeFiber from './three-types';
3
3
  export { ReactThreeFiber };
4
4
  export type { BaseInstance, LocalState } from './core/renderer';
5
- export type { Intersection, Subscription, Dpr, Size, Viewport, Camera, RenderCallback, Performance, RootState, } from './core/store';
6
- export type { ThreeEvent, Events, EventManager } from './core/events';
7
- export type { ObjectMap } from './core/utils';
5
+ export type { Intersection, Subscription, Dpr, Size, Viewport, RenderCallback, Performance, RootState, } from './core/store';
6
+ export type { ThreeEvent, Events, EventManager, ComputeFunction } from './core/events';
7
+ export type { ObjectMap, Camera } from './core/utils';
8
8
  export * from './native/Canvas';
9
9
  export { createTouchEvents as events } from './native/events';
10
10
  export * from './core';
@@ -39,9 +39,10 @@ var threeTypes = /*#__PURE__*/Object.freeze({
39
39
  __proto__: null
40
40
  });
41
41
 
42
- // React currently throws a warning when using useLayoutEffect on the server.
42
+ const isOrthographicCamera = def => def && def.isOrthographicCamera; // React currently throws a warning when using useLayoutEffect on the server.
43
43
  // To get around it, we can conditionally useEffect on the server (no-op) and
44
44
  // useLayoutEffect on the client.
45
+
45
46
  const isSSR = typeof window === 'undefined' || !window.navigator || /ServerSideRendering|^Deno\//.test(window.navigator.userAgent);
46
47
  const useIsomorphicLayoutEffect = isSSR ? React__namespace.useEffect : React__namespace.useLayoutEffect;
47
48
  function useMutableCallback(fn) {
@@ -416,6 +417,25 @@ function invalidateInstance(instance) {
416
417
  function updateInstance(instance) {
417
418
  instance.onUpdate == null ? void 0 : instance.onUpdate(instance);
418
419
  }
420
+ function updateCamera(camera, size) {
421
+ // https://github.com/pmndrs/react-three-fiber/issues/92
422
+ // Do not mess with the camera if it belongs to the user
423
+ if (!camera.manual) {
424
+ if (isOrthographicCamera(camera)) {
425
+ camera.left = size.width / -2;
426
+ camera.right = size.width / 2;
427
+ camera.top = size.height / 2;
428
+ camera.bottom = size.height / -2;
429
+ } else {
430
+ camera.aspect = size.width / size.height;
431
+ }
432
+
433
+ camera.updateProjectionMatrix(); // https://github.com/pmndrs/react-three-fiber/issues/178
434
+ // Update matrix world since the renderer is a frame late
435
+
436
+ camera.updateMatrixWorld();
437
+ }
438
+ }
419
439
 
420
440
  function makeId(event) {
421
441
  return (event.eventObject || event.object).uuid + '/' + event.index + event.instanceId;
@@ -1189,7 +1209,6 @@ function createRenderer(roots, getEventPriority) {
1189
1209
  }
1190
1210
 
1191
1211
  const isRenderer = def => !!(def != null && def.render);
1192
- const isOrthographicCamera = def => def && def.isOrthographicCamera;
1193
1212
  const context = /*#__PURE__*/React__namespace.createContext(null);
1194
1213
 
1195
1214
  const createStore = (invalidate, advance) => {
@@ -1396,24 +1415,7 @@ const createStore = (invalidate, advance) => {
1396
1415
  } = rootState.getState();
1397
1416
 
1398
1417
  if (size !== oldSize || viewport.dpr !== oldDpr) {
1399
- // https://github.com/pmndrs/react-three-fiber/issues/92
1400
- // Do not mess with the camera if it belongs to the user
1401
- if (!camera.manual) {
1402
- if (isOrthographicCamera(camera)) {
1403
- camera.left = size.width / -2;
1404
- camera.right = size.width / 2;
1405
- camera.top = size.height / 2;
1406
- camera.bottom = size.height / -2;
1407
- } else {
1408
- camera.aspect = size.width / size.height;
1409
- }
1410
-
1411
- camera.updateProjectionMatrix(); // https://github.com/pmndrs/react-three-fiber/issues/178
1412
- // Update matrix world since the renderer is a frame late
1413
-
1414
- camera.updateMatrixWorld();
1415
- } // Update renderer
1416
-
1418
+ updateCamera(camera, size); // Update renderer
1417
1419
 
1418
1420
  gl.setPixelRatio(viewport.dpr);
1419
1421
  gl.setSize(size.width, size.height);
@@ -1952,6 +1954,7 @@ function Portal({
1952
1954
  * {createPortal(...)} */
1953
1955
  const {
1954
1956
  events,
1957
+ size,
1955
1958
  ...rest
1956
1959
  } = state;
1957
1960
  const previousRoot = useStore();
@@ -1963,45 +1966,79 @@ function Portal({
1963
1966
 
1964
1967
  if (injectState) {
1965
1968
  // Only the fields of "state" that do not differ from injectState
1969
+ // Some props should be off-limits
1970
+ // Otherwise filter out the props that are different and let the inject layer take precedence
1966
1971
  Object.keys(state).forEach(key => {
1967
- if ( // Some props should be off-limits
1968
- !['size', 'viewport', 'internal', 'performance'].includes(key) && // Otherwise filter out the props that are different and let the inject layer take precedence
1969
- state[key] !== injectState[key]) delete intersect[key];
1972
+ if (state[key] !== injectState[key] && !['internal', 'performance'].includes(key)) {
1973
+ delete intersect[key];
1974
+ }
1970
1975
  });
1971
1976
  }
1972
1977
 
1973
- return { ...intersect,
1978
+ let viewport = undefined;
1979
+
1980
+ if (injectState && size) {
1981
+ const camera = injectState.camera; // Calculate the override viewport, if present
1982
+
1983
+ viewport = state.viewport.getCurrentViewport(camera, new THREE__namespace.Vector3(), size); // Update the portal camera, if it differs from the previous layer
1984
+
1985
+ if (camera !== state.camera) updateCamera(camera, size);
1986
+ }
1987
+
1988
+ return { // The intersect consists of the previous root state
1989
+ ...intersect,
1990
+ // Portals have their own scene, which forms the root, a raycaster and a pointer
1974
1991
  scene: container,
1975
- previousRoot,
1976
1992
  raycaster,
1993
+ pointer,
1994
+ mouse: pointer,
1995
+ // Their previous root is the layer before it
1996
+ previousRoot,
1997
+ // Events, size and viewport can be overridden by the inject layer
1977
1998
  events: { ...state.events,
1978
1999
  ...(injectState == null ? void 0 : injectState.events),
1979
- pointer,
1980
- mouse: pointer,
1981
2000
  ...events
1982
2001
  },
2002
+ size: { ...state.size,
2003
+ ...size
2004
+ },
2005
+ viewport: { ...state.viewport,
2006
+ ...viewport
2007
+ },
1983
2008
  ...rest
1984
2009
  };
1985
2010
  }, [state]);
1986
- const [useInjectStore] = React__namespace.useState(() => {
1987
- const store = create__default['default']((set, get) => ({ ...inject(previousRoot.getState()),
2011
+ const [usePortalStore] = React__namespace.useState(() => {
2012
+ // Create a mirrored store, based on the previous root with a few overrides ...
2013
+ new THREE__namespace.Vector3();
2014
+ const previousState = previousRoot.getState();
2015
+ const store = create__default['default']((set, get) => ({ ...inject(previousState),
2016
+ // Set and get refer to this root-state
1988
2017
  set,
1989
2018
  get,
2019
+ // Layers are allowed to override events
1990
2020
  setEvents: events => set(state => ({ ...state,
1991
2021
  events: { ...state.events,
1992
2022
  ...events
1993
2023
  }
1994
2024
  }))
1995
2025
  }));
1996
- previousRoot.subscribe(state => useInjectStore.setState(injectState => inject(state, injectState)));
1997
2026
  return store;
1998
2027
  });
1999
2028
  React__namespace.useEffect(() => {
2000
- useInjectStore.setState(injectState => inject(previousRoot.getState(), injectState));
2029
+ // Subscribe to previous root-state and copy changes over to the mirrored portal-state
2030
+ const unsub = previousRoot.subscribe(prev => usePortalStore.setState(state => inject(prev, state)));
2031
+ return () => {
2032
+ unsub();
2033
+ usePortalStore.destroy();
2034
+ };
2035
+ }, []);
2036
+ React__namespace.useEffect(() => {
2037
+ usePortalStore.setState(injectState => inject(previousRoot.getState(), injectState));
2001
2038
  }, [inject]);
2002
2039
  return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, reconciler.createPortal( /*#__PURE__*/React__namespace.createElement(context.Provider, {
2003
- value: useInjectStore
2004
- }, children), useInjectStore, null));
2040
+ value: usePortalStore
2041
+ }, children), usePortalStore, null));
2005
2042
  }
2006
2043
 
2007
2044
  reconciler.injectIntoDevTools({
@@ -10,9 +10,10 @@ var threeTypes = /*#__PURE__*/Object.freeze({
10
10
  __proto__: null
11
11
  });
12
12
 
13
- // React currently throws a warning when using useLayoutEffect on the server.
13
+ const isOrthographicCamera = def => def && def.isOrthographicCamera; // React currently throws a warning when using useLayoutEffect on the server.
14
14
  // To get around it, we can conditionally useEffect on the server (no-op) and
15
15
  // useLayoutEffect on the client.
16
+
16
17
  const isSSR = typeof window === 'undefined' || !window.navigator || /ServerSideRendering|^Deno\//.test(window.navigator.userAgent);
17
18
  const useIsomorphicLayoutEffect = isSSR ? React.useEffect : React.useLayoutEffect;
18
19
  function useMutableCallback(fn) {
@@ -387,6 +388,25 @@ function invalidateInstance(instance) {
387
388
  function updateInstance(instance) {
388
389
  instance.onUpdate == null ? void 0 : instance.onUpdate(instance);
389
390
  }
391
+ function updateCamera(camera, size) {
392
+ // https://github.com/pmndrs/react-three-fiber/issues/92
393
+ // Do not mess with the camera if it belongs to the user
394
+ if (!camera.manual) {
395
+ if (isOrthographicCamera(camera)) {
396
+ camera.left = size.width / -2;
397
+ camera.right = size.width / 2;
398
+ camera.top = size.height / 2;
399
+ camera.bottom = size.height / -2;
400
+ } else {
401
+ camera.aspect = size.width / size.height;
402
+ }
403
+
404
+ camera.updateProjectionMatrix(); // https://github.com/pmndrs/react-three-fiber/issues/178
405
+ // Update matrix world since the renderer is a frame late
406
+
407
+ camera.updateMatrixWorld();
408
+ }
409
+ }
390
410
 
391
411
  function makeId(event) {
392
412
  return (event.eventObject || event.object).uuid + '/' + event.index + event.instanceId;
@@ -1160,7 +1180,6 @@ function createRenderer(roots, getEventPriority) {
1160
1180
  }
1161
1181
 
1162
1182
  const isRenderer = def => !!(def != null && def.render);
1163
- const isOrthographicCamera = def => def && def.isOrthographicCamera;
1164
1183
  const context = /*#__PURE__*/React.createContext(null);
1165
1184
 
1166
1185
  const createStore = (invalidate, advance) => {
@@ -1367,24 +1386,7 @@ const createStore = (invalidate, advance) => {
1367
1386
  } = rootState.getState();
1368
1387
 
1369
1388
  if (size !== oldSize || viewport.dpr !== oldDpr) {
1370
- // https://github.com/pmndrs/react-three-fiber/issues/92
1371
- // Do not mess with the camera if it belongs to the user
1372
- if (!camera.manual) {
1373
- if (isOrthographicCamera(camera)) {
1374
- camera.left = size.width / -2;
1375
- camera.right = size.width / 2;
1376
- camera.top = size.height / 2;
1377
- camera.bottom = size.height / -2;
1378
- } else {
1379
- camera.aspect = size.width / size.height;
1380
- }
1381
-
1382
- camera.updateProjectionMatrix(); // https://github.com/pmndrs/react-three-fiber/issues/178
1383
- // Update matrix world since the renderer is a frame late
1384
-
1385
- camera.updateMatrixWorld();
1386
- } // Update renderer
1387
-
1389
+ updateCamera(camera, size); // Update renderer
1388
1390
 
1389
1391
  gl.setPixelRatio(viewport.dpr);
1390
1392
  gl.setSize(size.width, size.height);
@@ -1923,6 +1925,7 @@ function Portal({
1923
1925
  * {createPortal(...)} */
1924
1926
  const {
1925
1927
  events,
1928
+ size,
1926
1929
  ...rest
1927
1930
  } = state;
1928
1931
  const previousRoot = useStore();
@@ -1934,45 +1937,79 @@ function Portal({
1934
1937
 
1935
1938
  if (injectState) {
1936
1939
  // Only the fields of "state" that do not differ from injectState
1940
+ // Some props should be off-limits
1941
+ // Otherwise filter out the props that are different and let the inject layer take precedence
1937
1942
  Object.keys(state).forEach(key => {
1938
- if ( // Some props should be off-limits
1939
- !['size', 'viewport', 'internal', 'performance'].includes(key) && // Otherwise filter out the props that are different and let the inject layer take precedence
1940
- state[key] !== injectState[key]) delete intersect[key];
1943
+ if (state[key] !== injectState[key] && !['internal', 'performance'].includes(key)) {
1944
+ delete intersect[key];
1945
+ }
1941
1946
  });
1942
1947
  }
1943
1948
 
1944
- return { ...intersect,
1949
+ let viewport = undefined;
1950
+
1951
+ if (injectState && size) {
1952
+ const camera = injectState.camera; // Calculate the override viewport, if present
1953
+
1954
+ viewport = state.viewport.getCurrentViewport(camera, new THREE.Vector3(), size); // Update the portal camera, if it differs from the previous layer
1955
+
1956
+ if (camera !== state.camera) updateCamera(camera, size);
1957
+ }
1958
+
1959
+ return { // The intersect consists of the previous root state
1960
+ ...intersect,
1961
+ // Portals have their own scene, which forms the root, a raycaster and a pointer
1945
1962
  scene: container,
1946
- previousRoot,
1947
1963
  raycaster,
1964
+ pointer,
1965
+ mouse: pointer,
1966
+ // Their previous root is the layer before it
1967
+ previousRoot,
1968
+ // Events, size and viewport can be overridden by the inject layer
1948
1969
  events: { ...state.events,
1949
1970
  ...(injectState == null ? void 0 : injectState.events),
1950
- pointer,
1951
- mouse: pointer,
1952
1971
  ...events
1953
1972
  },
1973
+ size: { ...state.size,
1974
+ ...size
1975
+ },
1976
+ viewport: { ...state.viewport,
1977
+ ...viewport
1978
+ },
1954
1979
  ...rest
1955
1980
  };
1956
1981
  }, [state]);
1957
- const [useInjectStore] = React.useState(() => {
1958
- const store = create((set, get) => ({ ...inject(previousRoot.getState()),
1982
+ const [usePortalStore] = React.useState(() => {
1983
+ // Create a mirrored store, based on the previous root with a few overrides ...
1984
+ new THREE.Vector3();
1985
+ const previousState = previousRoot.getState();
1986
+ const store = create((set, get) => ({ ...inject(previousState),
1987
+ // Set and get refer to this root-state
1959
1988
  set,
1960
1989
  get,
1990
+ // Layers are allowed to override events
1961
1991
  setEvents: events => set(state => ({ ...state,
1962
1992
  events: { ...state.events,
1963
1993
  ...events
1964
1994
  }
1965
1995
  }))
1966
1996
  }));
1967
- previousRoot.subscribe(state => useInjectStore.setState(injectState => inject(state, injectState)));
1968
1997
  return store;
1969
1998
  });
1970
1999
  React.useEffect(() => {
1971
- useInjectStore.setState(injectState => inject(previousRoot.getState(), injectState));
2000
+ // Subscribe to previous root-state and copy changes over to the mirrored portal-state
2001
+ const unsub = previousRoot.subscribe(prev => usePortalStore.setState(state => inject(prev, state)));
2002
+ return () => {
2003
+ unsub();
2004
+ usePortalStore.destroy();
2005
+ };
2006
+ }, []);
2007
+ React.useEffect(() => {
2008
+ usePortalStore.setState(injectState => inject(previousRoot.getState(), injectState));
1972
2009
  }, [inject]);
1973
2010
  return /*#__PURE__*/React.createElement(React.Fragment, null, reconciler.createPortal( /*#__PURE__*/React.createElement(context.Provider, {
1974
- value: useInjectStore
1975
- }, children), useInjectStore, null));
2011
+ value: usePortalStore
2012
+ }, children), usePortalStore, null));
1976
2013
  }
1977
2014
 
1978
2015
  reconciler.injectIntoDevTools({
@@ -39,9 +39,10 @@ var threeTypes = /*#__PURE__*/Object.freeze({
39
39
  __proto__: null
40
40
  });
41
41
 
42
- // React currently throws a warning when using useLayoutEffect on the server.
42
+ const isOrthographicCamera = def => def && def.isOrthographicCamera; // React currently throws a warning when using useLayoutEffect on the server.
43
43
  // To get around it, we can conditionally useEffect on the server (no-op) and
44
44
  // useLayoutEffect on the client.
45
+
45
46
  const isSSR = typeof window === 'undefined' || !window.navigator || /ServerSideRendering|^Deno\//.test(window.navigator.userAgent);
46
47
  const useIsomorphicLayoutEffect = isSSR ? React__namespace.useEffect : React__namespace.useLayoutEffect;
47
48
  function useMutableCallback(fn) {
@@ -416,6 +417,25 @@ function invalidateInstance(instance) {
416
417
  function updateInstance(instance) {
417
418
  instance.onUpdate == null ? void 0 : instance.onUpdate(instance);
418
419
  }
420
+ function updateCamera(camera, size) {
421
+ // https://github.com/pmndrs/react-three-fiber/issues/92
422
+ // Do not mess with the camera if it belongs to the user
423
+ if (!camera.manual) {
424
+ if (isOrthographicCamera(camera)) {
425
+ camera.left = size.width / -2;
426
+ camera.right = size.width / 2;
427
+ camera.top = size.height / 2;
428
+ camera.bottom = size.height / -2;
429
+ } else {
430
+ camera.aspect = size.width / size.height;
431
+ }
432
+
433
+ camera.updateProjectionMatrix(); // https://github.com/pmndrs/react-three-fiber/issues/178
434
+ // Update matrix world since the renderer is a frame late
435
+
436
+ camera.updateMatrixWorld();
437
+ }
438
+ }
419
439
 
420
440
  function makeId(event) {
421
441
  return (event.eventObject || event.object).uuid + '/' + event.index + event.instanceId;
@@ -1189,7 +1209,6 @@ function createRenderer(roots, getEventPriority) {
1189
1209
  }
1190
1210
 
1191
1211
  const isRenderer = def => !!(def != null && def.render);
1192
- const isOrthographicCamera = def => def && def.isOrthographicCamera;
1193
1212
  const context = /*#__PURE__*/React__namespace.createContext(null);
1194
1213
 
1195
1214
  const createStore = (invalidate, advance) => {
@@ -1396,24 +1415,7 @@ const createStore = (invalidate, advance) => {
1396
1415
  } = rootState.getState();
1397
1416
 
1398
1417
  if (size !== oldSize || viewport.dpr !== oldDpr) {
1399
- // https://github.com/pmndrs/react-three-fiber/issues/92
1400
- // Do not mess with the camera if it belongs to the user
1401
- if (!camera.manual) {
1402
- if (isOrthographicCamera(camera)) {
1403
- camera.left = size.width / -2;
1404
- camera.right = size.width / 2;
1405
- camera.top = size.height / 2;
1406
- camera.bottom = size.height / -2;
1407
- } else {
1408
- camera.aspect = size.width / size.height;
1409
- }
1410
-
1411
- camera.updateProjectionMatrix(); // https://github.com/pmndrs/react-three-fiber/issues/178
1412
- // Update matrix world since the renderer is a frame late
1413
-
1414
- camera.updateMatrixWorld();
1415
- } // Update renderer
1416
-
1418
+ updateCamera(camera, size); // Update renderer
1417
1419
 
1418
1420
  gl.setPixelRatio(viewport.dpr);
1419
1421
  gl.setSize(size.width, size.height);
@@ -1952,6 +1954,7 @@ function Portal({
1952
1954
  * {createPortal(...)} */
1953
1955
  const {
1954
1956
  events,
1957
+ size,
1955
1958
  ...rest
1956
1959
  } = state;
1957
1960
  const previousRoot = useStore();
@@ -1963,45 +1966,79 @@ function Portal({
1963
1966
 
1964
1967
  if (injectState) {
1965
1968
  // Only the fields of "state" that do not differ from injectState
1969
+ // Some props should be off-limits
1970
+ // Otherwise filter out the props that are different and let the inject layer take precedence
1966
1971
  Object.keys(state).forEach(key => {
1967
- if ( // Some props should be off-limits
1968
- !['size', 'viewport', 'internal', 'performance'].includes(key) && // Otherwise filter out the props that are different and let the inject layer take precedence
1969
- state[key] !== injectState[key]) delete intersect[key];
1972
+ if (state[key] !== injectState[key] && !['internal', 'performance'].includes(key)) {
1973
+ delete intersect[key];
1974
+ }
1970
1975
  });
1971
1976
  }
1972
1977
 
1973
- return { ...intersect,
1978
+ let viewport = undefined;
1979
+
1980
+ if (injectState && size) {
1981
+ const camera = injectState.camera; // Calculate the override viewport, if present
1982
+
1983
+ viewport = state.viewport.getCurrentViewport(camera, new THREE__namespace.Vector3(), size); // Update the portal camera, if it differs from the previous layer
1984
+
1985
+ if (camera !== state.camera) updateCamera(camera, size);
1986
+ }
1987
+
1988
+ return { // The intersect consists of the previous root state
1989
+ ...intersect,
1990
+ // Portals have their own scene, which forms the root, a raycaster and a pointer
1974
1991
  scene: container,
1975
- previousRoot,
1976
1992
  raycaster,
1993
+ pointer,
1994
+ mouse: pointer,
1995
+ // Their previous root is the layer before it
1996
+ previousRoot,
1997
+ // Events, size and viewport can be overridden by the inject layer
1977
1998
  events: { ...state.events,
1978
1999
  ...(injectState == null ? void 0 : injectState.events),
1979
- pointer,
1980
- mouse: pointer,
1981
2000
  ...events
1982
2001
  },
2002
+ size: { ...state.size,
2003
+ ...size
2004
+ },
2005
+ viewport: { ...state.viewport,
2006
+ ...viewport
2007
+ },
1983
2008
  ...rest
1984
2009
  };
1985
2010
  }, [state]);
1986
- const [useInjectStore] = React__namespace.useState(() => {
1987
- const store = create__default['default']((set, get) => ({ ...inject(previousRoot.getState()),
2011
+ const [usePortalStore] = React__namespace.useState(() => {
2012
+ // Create a mirrored store, based on the previous root with a few overrides ...
2013
+ new THREE__namespace.Vector3();
2014
+ const previousState = previousRoot.getState();
2015
+ const store = create__default['default']((set, get) => ({ ...inject(previousState),
2016
+ // Set and get refer to this root-state
1988
2017
  set,
1989
2018
  get,
2019
+ // Layers are allowed to override events
1990
2020
  setEvents: events => set(state => ({ ...state,
1991
2021
  events: { ...state.events,
1992
2022
  ...events
1993
2023
  }
1994
2024
  }))
1995
2025
  }));
1996
- previousRoot.subscribe(state => useInjectStore.setState(injectState => inject(state, injectState)));
1997
2026
  return store;
1998
2027
  });
1999
2028
  React__namespace.useEffect(() => {
2000
- useInjectStore.setState(injectState => inject(previousRoot.getState(), injectState));
2029
+ // Subscribe to previous root-state and copy changes over to the mirrored portal-state
2030
+ const unsub = previousRoot.subscribe(prev => usePortalStore.setState(state => inject(prev, state)));
2031
+ return () => {
2032
+ unsub();
2033
+ usePortalStore.destroy();
2034
+ };
2035
+ }, []);
2036
+ React__namespace.useEffect(() => {
2037
+ usePortalStore.setState(injectState => inject(previousRoot.getState(), injectState));
2001
2038
  }, [inject]);
2002
2039
  return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, reconciler.createPortal( /*#__PURE__*/React__namespace.createElement(context.Provider, {
2003
- value: useInjectStore
2004
- }, children), useInjectStore, null));
2040
+ value: usePortalStore
2041
+ }, children), usePortalStore, null));
2005
2042
  }
2006
2043
 
2007
2044
  reconciler.injectIntoDevTools({
@@ -2,7 +2,7 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var index = require('./index-49237346.cjs.dev.js');
5
+ var index = require('./index-018d2d45.cjs.dev.js');
6
6
  var _extends = require('@babel/runtime/helpers/extends');
7
7
  var React = require('react');
8
8
  var THREE = require('three');
@@ -149,8 +149,8 @@ const Canvas = /*#__PURE__*/React__namespace.forwardRef(function Canvas({
149
149
  },
150
150
  ...resize
151
151
  });
152
- const meshRef = React__namespace.useRef(null);
153
152
  const canvasRef = React__namespace.useRef(null);
153
+ const meshRef = React__namespace.useRef(null);
154
154
  const [canvas, setCanvas] = React__namespace.useState(null);
155
155
  const handlePointerMissed = index.useMutableCallback(onPointerMissed);
156
156
  const canvasProps = index.pick(props, CANVAS_PROPS);
@@ -191,8 +191,8 @@ const Canvas = /*#__PURE__*/React__namespace.forwardRef(function Canvas({
191
191
  setCanvas(canvasRef.current);
192
192
  }, []);
193
193
  React__namespace.useEffect(() => {
194
- return () => index.unmountComponentAtNode(canvas);
195
- }, []);
194
+ if (canvas) return () => index.unmountComponentAtNode(canvas);
195
+ }, [canvas]);
196
196
  return /*#__PURE__*/React__namespace.createElement("div", _extends({
197
197
  ref: mergeRefs__default['default']([meshRef, containerRef]),
198
198
  style: {
@@ -2,7 +2,7 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var index = require('./index-4dae9cc6.cjs.prod.js');
5
+ var index = require('./index-c8c0474b.cjs.prod.js');
6
6
  var _extends = require('@babel/runtime/helpers/extends');
7
7
  var React = require('react');
8
8
  var THREE = require('three');
@@ -149,8 +149,8 @@ const Canvas = /*#__PURE__*/React__namespace.forwardRef(function Canvas({
149
149
  },
150
150
  ...resize
151
151
  });
152
- const meshRef = React__namespace.useRef(null);
153
152
  const canvasRef = React__namespace.useRef(null);
153
+ const meshRef = React__namespace.useRef(null);
154
154
  const [canvas, setCanvas] = React__namespace.useState(null);
155
155
  const handlePointerMissed = index.useMutableCallback(onPointerMissed);
156
156
  const canvasProps = index.pick(props, CANVAS_PROPS);
@@ -191,8 +191,8 @@ const Canvas = /*#__PURE__*/React__namespace.forwardRef(function Canvas({
191
191
  setCanvas(canvasRef.current);
192
192
  }, []);
193
193
  React__namespace.useEffect(() => {
194
- return () => index.unmountComponentAtNode(canvas);
195
- }, []);
194
+ if (canvas) return () => index.unmountComponentAtNode(canvas);
195
+ }, [canvas]);
196
196
  return /*#__PURE__*/React__namespace.createElement("div", _extends({
197
197
  ref: mergeRefs__default['default']([meshRef, containerRef]),
198
198
  style: {
@@ -1,5 +1,5 @@
1
- import { c as createEvents, e as extend, u as useMutableCallback, p as pick, o as omit, a as createRoot, E as ErrorBoundary, B as Block, b as useIsomorphicLayoutEffect, d as unmountComponentAtNode } from './index-4e14a95b.esm.js';
2
- export { t as ReactThreeFiber, w as _roots, v as act, n as addAfterEffect, m as addEffect, q as addTail, l as advance, i as applyProps, f as context, g as createPortal, a as createRoot, j as dispose, e as extend, s as getRootState, k as invalidate, h as reconciler, r as render, d as unmountComponentAtNode, z as useFrame, A as useGraph, C as useLoader, x as useStore, y as useThree } from './index-4e14a95b.esm.js';
1
+ import { c as createEvents, e as extend, u as useMutableCallback, p as pick, o as omit, a as createRoot, E as ErrorBoundary, B as Block, b as useIsomorphicLayoutEffect, d as unmountComponentAtNode } from './index-91152509.esm.js';
2
+ export { t as ReactThreeFiber, w as _roots, v as act, n as addAfterEffect, m as addEffect, q as addTail, l as advance, i as applyProps, f as context, g as createPortal, a as createRoot, j as dispose, e as extend, s as getRootState, k as invalidate, h as reconciler, r as render, d as unmountComponentAtNode, z as useFrame, A as useGraph, C as useLoader, x as useStore, y as useThree } from './index-91152509.esm.js';
3
3
  import _extends from '@babel/runtime/helpers/esm/extends';
4
4
  import * as React from 'react';
5
5
  import * as THREE from 'three';
@@ -119,8 +119,8 @@ const Canvas = /*#__PURE__*/React.forwardRef(function Canvas({
119
119
  },
120
120
  ...resize
121
121
  });
122
- const meshRef = React.useRef(null);
123
122
  const canvasRef = React.useRef(null);
123
+ const meshRef = React.useRef(null);
124
124
  const [canvas, setCanvas] = React.useState(null);
125
125
  const handlePointerMissed = useMutableCallback(onPointerMissed);
126
126
  const canvasProps = pick(props, CANVAS_PROPS);
@@ -161,8 +161,8 @@ const Canvas = /*#__PURE__*/React.forwardRef(function Canvas({
161
161
  setCanvas(canvasRef.current);
162
162
  }, []);
163
163
  React.useEffect(() => {
164
- return () => unmountComponentAtNode(canvas);
165
- }, []);
164
+ if (canvas) return () => unmountComponentAtNode(canvas);
165
+ }, [canvas]);
166
166
  return /*#__PURE__*/React.createElement("div", _extends({
167
167
  ref: mergeRefs([meshRef, containerRef]),
168
168
  style: {
@@ -4,7 +4,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var THREE = require('three');
6
6
  var expoAsset = require('expo-asset');
7
- var index = require('../../dist/index-49237346.cjs.dev.js');
7
+ var index = require('../../dist/index-018d2d45.cjs.dev.js');
8
8
  var _extends = require('@babel/runtime/helpers/extends');
9
9
  var React = require('react');
10
10
  var mergeRefs = require('react-merge-refs');
@@ -229,8 +229,10 @@ const Canvas = /*#__PURE__*/React__namespace.forwardRef(({
229
229
  }
230
230
 
231
231
  React__namespace.useEffect(() => {
232
- return () => index.unmountComponentAtNode(canvas);
233
- }, []);
232
+ if (canvas) {
233
+ return () => index.unmountComponentAtNode(canvas);
234
+ }
235
+ }, [canvas]);
234
236
  return /*#__PURE__*/React__namespace.createElement(reactNative.View, _extends({}, viewProps, {
235
237
  ref: mergeRefs__default['default']([viewRef, forwardedRef]),
236
238
  onLayout: onLayout,
@@ -4,7 +4,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var THREE = require('three');
6
6
  var expoAsset = require('expo-asset');
7
- var index = require('../../dist/index-4dae9cc6.cjs.prod.js');
7
+ var index = require('../../dist/index-c8c0474b.cjs.prod.js');
8
8
  var _extends = require('@babel/runtime/helpers/extends');
9
9
  var React = require('react');
10
10
  var mergeRefs = require('react-merge-refs');
@@ -229,8 +229,10 @@ const Canvas = /*#__PURE__*/React__namespace.forwardRef(({
229
229
  }
230
230
 
231
231
  React__namespace.useEffect(() => {
232
- return () => index.unmountComponentAtNode(canvas);
233
- }, []);
232
+ if (canvas) {
233
+ return () => index.unmountComponentAtNode(canvas);
234
+ }
235
+ }, [canvas]);
234
236
  return /*#__PURE__*/React__namespace.createElement(reactNative.View, _extends({}, viewProps, {
235
237
  ref: mergeRefs__default['default']([viewRef, forwardedRef]),
236
238
  onLayout: onLayout,
@@ -1,7 +1,7 @@
1
1
  import * as THREE from 'three';
2
2
  import { Asset } from 'expo-asset';
3
- import { c as createEvents, e as extend, u as useMutableCallback, p as pick, o as omit, a as createRoot, E as ErrorBoundary, B as Block, d as unmountComponentAtNode } from '../../dist/index-4e14a95b.esm.js';
4
- export { t as ReactThreeFiber, w as _roots, v as act, n as addAfterEffect, m as addEffect, q as addTail, l as advance, i as applyProps, f as context, g as createPortal, a as createRoot, j as dispose, e as extend, s as getRootState, k as invalidate, h as reconciler, r as render, d as unmountComponentAtNode, z as useFrame, A as useGraph, C as useLoader, x as useStore, y as useThree } from '../../dist/index-4e14a95b.esm.js';
3
+ import { c as createEvents, e as extend, u as useMutableCallback, p as pick, o as omit, a as createRoot, E as ErrorBoundary, B as Block, d as unmountComponentAtNode } from '../../dist/index-91152509.esm.js';
4
+ export { t as ReactThreeFiber, w as _roots, v as act, n as addAfterEffect, m as addEffect, q as addTail, l as advance, i as applyProps, f as context, g as createPortal, a as createRoot, j as dispose, e as extend, s as getRootState, k as invalidate, h as reconciler, r as render, d as unmountComponentAtNode, z as useFrame, A as useGraph, C as useLoader, x as useStore, y as useThree } from '../../dist/index-91152509.esm.js';
5
5
  import _extends from '@babel/runtime/helpers/esm/extends';
6
6
  import * as React from 'react';
7
7
  import mergeRefs from 'react-merge-refs';
@@ -199,8 +199,10 @@ const Canvas = /*#__PURE__*/React.forwardRef(({
199
199
  }
200
200
 
201
201
  React.useEffect(() => {
202
- return () => unmountComponentAtNode(canvas);
203
- }, []);
202
+ if (canvas) {
203
+ return () => unmountComponentAtNode(canvas);
204
+ }
205
+ }, [canvas]);
204
206
  return /*#__PURE__*/React.createElement(View, _extends({}, viewProps, {
205
207
  ref: mergeRefs([viewRef, forwardedRef]),
206
208
  onLayout: onLayout,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-three/fiber",
3
- "version": "8.0.9",
3
+ "version": "8.0.10",
4
4
  "description": "A React renderer for Threejs",
5
5
  "keywords": [
6
6
  "react",