@react-three/fiber 8.8.10 → 8.9.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @react-three/fiber
2
2
 
3
+ ## 8.9.0
4
+
5
+ ### Minor Changes
6
+
7
+ - a458b4dd: fix(loop): export flush methods and types
8
+
9
+ ## 8.8.11
10
+
11
+ ### Patch Changes
12
+
13
+ - 2068f0cc: fix: events pointerlock, useLoader extension types
14
+
3
15
  ## 8.8.10
4
16
 
5
17
  ### Patch Changes
@@ -4,13 +4,16 @@ import { StateSelector, EqualityChecker } from 'zustand';
4
4
  import { GLTF } from 'three/examples/jsm/loaders/GLTFLoader';
5
5
  import { RootState, RenderCallback } from './store';
6
6
  import { ObjectMap } from './utils';
7
- import { LoadingManager } from 'three';
8
7
  import { LocalState } from './renderer';
9
8
  export interface Loader<T> extends THREE.Loader {
10
9
  load(url: string, onLoad?: (result: T) => void, onProgress?: (event: ProgressEvent) => void, onError?: (event: ErrorEvent) => void): unknown;
11
10
  }
12
- export declare type Extensions = (loader: THREE.Loader) => void;
11
+ export declare type LoaderProto<T> = new (...args: any) => Loader<T extends unknown ? any : T>;
12
+ export declare type LoaderReturnType<T, L extends LoaderProto<T>> = T extends unknown ? Awaited<ReturnType<InstanceType<L>['loadAsync']>> : T;
13
13
  export declare type LoaderResult<T> = T extends any[] ? Loader<T[number]> : Loader<T>;
14
+ export declare type Extensions<T extends {
15
+ prototype: LoaderProto<any>;
16
+ }> = (loader: T['prototype']) => void;
14
17
  export declare type ConditionalType<Child, Parent, Truthy, Falsy> = Child extends Parent ? Truthy : Falsy;
15
18
  export declare type BranchingReturn<T, Parent, Coerced> = ConditionalType<T, Parent, Coerced, T>;
16
19
  export declare function useInstanceHandle<O>(ref: React.MutableRefObject<O>): React.MutableRefObject<LocalState>;
@@ -18,8 +21,8 @@ export declare function useStore(): import("zustand").UseBoundStore<RootState, i
18
21
  export declare function useThree<T = RootState>(selector?: StateSelector<RootState, T>, equalityFn?: EqualityChecker<T>): T;
19
22
  export declare function useFrame(callback: RenderCallback, renderPriority?: number): null;
20
23
  export declare function useGraph(object: THREE.Object3D): ObjectMap;
21
- export declare function useLoader<T, U extends string | string[]>(Proto: new (manager?: LoadingManager) => 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
+ export declare function useLoader<T, U extends string | string[], L extends LoaderProto<T>, R = LoaderReturnType<T, L>>(Proto: L, input: U, extensions?: Extensions<L>, onProgress?: (event: ProgressEvent<EventTarget>) => void): U extends any[] ? BranchingReturn<R, GLTF, GLTF & ObjectMap>[] : BranchingReturn<R, GLTF, GLTF & ObjectMap>;
22
25
  export declare namespace useLoader {
23
- var preload: <T, U extends string | string[]>(Proto: new () => LoaderResult<T>, input: U, extensions?: Extensions | undefined) => undefined;
24
- var clear: <T, U extends string | string[]>(Proto: new () => LoaderResult<T>, input: U) => void;
26
+ var preload: <T, U extends string | string[], L extends LoaderProto<T>>(Proto: L, input: U, extensions?: Extensions<L> | undefined) => undefined;
27
+ var clear: <T, U extends string | string[], L extends LoaderProto<T>>(Proto: L, input: U) => void;
25
28
  }
@@ -5,7 +5,7 @@ import { UseBoundStore } from 'zustand';
5
5
  import * as ReactThreeFiber from '../three-types';
6
6
  import { Renderer, context, RootState, Size, Dpr, Performance, PrivateKeys } from './store';
7
7
  import { extend, Root } from './renderer';
8
- import { addEffect, addAfterEffect, addTail } from './loop';
8
+ import { addEffect, addAfterEffect, addTail, flushGlobalEffects } from './loop';
9
9
  import { EventManager, ComputeFunction } from './events';
10
10
  import { dispose, getRootState, Camera } from './utils';
11
11
  declare const roots: Map<Element, Root>;
@@ -54,4 +54,4 @@ export declare type InjectState = Partial<Omit<RootState, PrivateKeys> & {
54
54
  declare function createPortal(children: React.ReactNode, container: THREE.Object3D, state?: InjectState): JSX.Element;
55
55
  declare const act: any;
56
56
  export * from './hooks';
57
- export { context, render, createRoot, unmountComponentAtNode, createPortal, reconciler, applyProps, dispose, invalidate, advance, extend, addEffect, addAfterEffect, addTail, getRootState, act, roots as _roots, };
57
+ export { context, render, createRoot, unmountComponentAtNode, createPortal, reconciler, applyProps, dispose, invalidate, advance, extend, addEffect, addAfterEffect, addTail, flushGlobalEffects, getRootState, act, roots as _roots, };
@@ -1,7 +1,7 @@
1
1
  import * as THREE from 'three';
2
2
  import { Root } from './renderer';
3
3
  import { RootState } from './store';
4
- declare type GlobalRenderCallback = (timeStamp: number) => void;
4
+ export declare type GlobalRenderCallback = (timeStamp: number) => void;
5
5
  export declare const addEffect: (callback: GlobalRenderCallback) => () => void;
6
6
  export declare const addAfterEffect: (callback: GlobalRenderCallback) => () => void;
7
7
  export declare const addTail: (callback: GlobalRenderCallback) => () => void;
@@ -12,4 +12,3 @@ export declare function createLoop<TCanvas>(roots: Map<TCanvas, Root>): {
12
12
  invalidate: (state?: RootState | undefined, frames?: number) => void;
13
13
  advance: (timestamp: number, runGlobalEffects?: boolean, state?: RootState | undefined, frame?: THREE.XRFrame | undefined) => void;
14
14
  };
15
- export {};
@@ -4,8 +4,9 @@ export { ReactThreeFiber };
4
4
  export type { BaseInstance, LocalState } from './core/renderer';
5
5
  export type { Intersection, Subscription, Dpr, Size, Viewport, RenderCallback, Performance, RootState, } from './core/store';
6
6
  export type { ThreeEvent, Events, EventManager, ComputeFunction } from './core/events';
7
+ export { createEvents } from './core/events';
7
8
  export type { ObjectMap, Camera } from './core/utils';
8
9
  export * from './web/Canvas';
9
- export { createEvents } from './core/events';
10
10
  export { createPointerEvents as events } from './web/events';
11
+ export type { GlobalRenderCallback, GlobalEffectType } from './core/loop';
11
12
  export * from './core';
@@ -4,7 +4,9 @@ export { ReactThreeFiber };
4
4
  export type { BaseInstance, LocalState } from './core/renderer';
5
5
  export type { Intersection, Subscription, Dpr, Size, Viewport, RenderCallback, Performance, RootState, } from './core/store';
6
6
  export type { ThreeEvent, Events, EventManager, ComputeFunction } from './core/events';
7
+ export { createEvents } from './core/events';
7
8
  export type { ObjectMap, Camera } from './core/utils';
8
9
  export * from './native/Canvas';
9
10
  export { createTouchEvents as events } from './native/events';
11
+ export type { GlobalRenderCallback, GlobalEffectType } from './core/loop';
10
12
  export * from './core';
@@ -579,7 +579,7 @@ function createEvents(store) {
579
579
  .sort((a, b) => {
580
580
  const aState = getRootState(a.object);
581
581
  const bState = getRootState(b.object);
582
- if (!aState || !bState) return 0;
582
+ if (!aState || !bState) return a.distance - b.distance;
583
583
  return bState.events.priority - aState.events.priority || a.distance - b.distance;
584
584
  }) // Filter out duplicates
585
585
  .filter(item => {
@@ -608,7 +608,7 @@ function createEvents(store) {
608
608
 
609
609
  if ('pointerId' in event && state.internal.capturedMap.has(event.pointerId)) {
610
610
  for (let captureData of state.internal.capturedMap.get(event.pointerId).values()) {
611
- intersections.push(captureData.intersection);
611
+ if (!duplicates.has(makeId(captureData.intersection))) intersections.push(captureData.intersection);
612
612
  }
613
613
  }
614
614
 
@@ -777,7 +777,7 @@ function createEvents(store) {
777
777
  internal
778
778
  } = store.getState();
779
779
 
780
- if ('pointerId' in event && !internal.capturedMap.has(event.pointerId)) {
780
+ if ('pointerId' in event && internal.capturedMap.has(event.pointerId)) {
781
781
  // If the object event interface had onLostPointerCapture, we'd call it here on every
782
782
  // object that's getting removed.
783
783
  internal.capturedMap.delete(event.pointerId);
@@ -2195,6 +2195,7 @@ exports.createPortal = createPortal;
2195
2195
  exports.createRoot = createRoot;
2196
2196
  exports.dispose = dispose;
2197
2197
  exports.extend = extend;
2198
+ exports.flushGlobalEffects = flushGlobalEffects;
2198
2199
  exports.getRootState = getRootState;
2199
2200
  exports.invalidate = invalidate;
2200
2201
  exports.isRef = isRef;
@@ -552,7 +552,7 @@ function createEvents(store) {
552
552
  .sort((a, b) => {
553
553
  const aState = getRootState(a.object);
554
554
  const bState = getRootState(b.object);
555
- if (!aState || !bState) return 0;
555
+ if (!aState || !bState) return a.distance - b.distance;
556
556
  return bState.events.priority - aState.events.priority || a.distance - b.distance;
557
557
  }) // Filter out duplicates
558
558
  .filter(item => {
@@ -581,7 +581,7 @@ function createEvents(store) {
581
581
 
582
582
  if ('pointerId' in event && state.internal.capturedMap.has(event.pointerId)) {
583
583
  for (let captureData of state.internal.capturedMap.get(event.pointerId).values()) {
584
- intersections.push(captureData.intersection);
584
+ if (!duplicates.has(makeId(captureData.intersection))) intersections.push(captureData.intersection);
585
585
  }
586
586
  }
587
587
 
@@ -750,7 +750,7 @@ function createEvents(store) {
750
750
  internal
751
751
  } = store.getState();
752
752
 
753
- if ('pointerId' in event && !internal.capturedMap.has(event.pointerId)) {
753
+ if ('pointerId' in event && internal.capturedMap.has(event.pointerId)) {
754
754
  // If the object event interface had onLostPointerCapture, we'd call it here on every
755
755
  // object that's getting removed.
756
756
  internal.capturedMap.delete(event.pointerId);
@@ -2154,4 +2154,4 @@ reconciler.injectIntoDevTools({
2154
2154
  });
2155
2155
  const act = React.unstable_act;
2156
2156
 
2157
- export { useGraph as A, Block as B, useLoader as C, ErrorBoundary as E, createRoot as a, useIsomorphicLayoutEffect as b, createEvents as c, unmountComponentAtNode as d, extend as e, context as f, createPortal as g, reconciler as h, isRef as i, applyProps as j, dispose as k, invalidate as l, advance as m, addEffect as n, addAfterEffect as o, addTail as p, getRootState as q, render as r, act as s, threeTypes as t, useMutableCallback as u, roots as v, useInstanceHandle as w, useStore as x, useThree as y, useFrame as z };
2157
+ export { useFrame as A, Block as B, useGraph as C, useLoader as D, ErrorBoundary as E, createRoot as a, useIsomorphicLayoutEffect as b, createEvents as c, unmountComponentAtNode as d, extend as e, context as f, createPortal as g, reconciler as h, isRef as i, applyProps as j, dispose as k, invalidate as l, advance as m, addEffect as n, addAfterEffect as o, addTail as p, flushGlobalEffects as q, render as r, getRootState as s, threeTypes as t, useMutableCallback as u, act as v, roots as w, useInstanceHandle as x, useStore as y, useThree as z };
@@ -579,7 +579,7 @@ function createEvents(store) {
579
579
  .sort((a, b) => {
580
580
  const aState = getRootState(a.object);
581
581
  const bState = getRootState(b.object);
582
- if (!aState || !bState) return 0;
582
+ if (!aState || !bState) return a.distance - b.distance;
583
583
  return bState.events.priority - aState.events.priority || a.distance - b.distance;
584
584
  }) // Filter out duplicates
585
585
  .filter(item => {
@@ -608,7 +608,7 @@ function createEvents(store) {
608
608
 
609
609
  if ('pointerId' in event && state.internal.capturedMap.has(event.pointerId)) {
610
610
  for (let captureData of state.internal.capturedMap.get(event.pointerId).values()) {
611
- intersections.push(captureData.intersection);
611
+ if (!duplicates.has(makeId(captureData.intersection))) intersections.push(captureData.intersection);
612
612
  }
613
613
  }
614
614
 
@@ -777,7 +777,7 @@ function createEvents(store) {
777
777
  internal
778
778
  } = store.getState();
779
779
 
780
- if ('pointerId' in event && !internal.capturedMap.has(event.pointerId)) {
780
+ if ('pointerId' in event && internal.capturedMap.has(event.pointerId)) {
781
781
  // If the object event interface had onLostPointerCapture, we'd call it here on every
782
782
  // object that's getting removed.
783
783
  internal.capturedMap.delete(event.pointerId);
@@ -2195,6 +2195,7 @@ exports.createPortal = createPortal;
2195
2195
  exports.createRoot = createRoot;
2196
2196
  exports.dispose = dispose;
2197
2197
  exports.extend = extend;
2198
+ exports.flushGlobalEffects = flushGlobalEffects;
2198
2199
  exports.getRootState = getRootState;
2199
2200
  exports.invalidate = invalidate;
2200
2201
  exports.isRef = isRef;
@@ -2,7 +2,7 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var index = require('./index-f307fe93.cjs.dev.js');
5
+ var index = require('./index-8539ced2.cjs.dev.js');
6
6
  var _extends = require('@babel/runtime/helpers/extends');
7
7
  var React = require('react');
8
8
  var THREE = require('three');
@@ -268,6 +268,7 @@ exports.createPortal = index.createPortal;
268
268
  exports.createRoot = index.createRoot;
269
269
  exports.dispose = index.dispose;
270
270
  exports.extend = index.extend;
271
+ exports.flushGlobalEffects = index.flushGlobalEffects;
271
272
  exports.getRootState = index.getRootState;
272
273
  exports.invalidate = index.invalidate;
273
274
  exports.reconciler = index.reconciler;
@@ -2,7 +2,7 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var index = require('./index-bd4f185a.cjs.prod.js');
5
+ var index = require('./index-57cb2f85.cjs.prod.js');
6
6
  var _extends = require('@babel/runtime/helpers/extends');
7
7
  var React = require('react');
8
8
  var THREE = require('three');
@@ -268,6 +268,7 @@ exports.createPortal = index.createPortal;
268
268
  exports.createRoot = index.createRoot;
269
269
  exports.dispose = index.dispose;
270
270
  exports.extend = index.extend;
271
+ exports.flushGlobalEffects = index.flushGlobalEffects;
271
272
  exports.getRootState = index.getRootState;
272
273
  exports.invalidate = index.invalidate;
273
274
  exports.reconciler = index.reconciler;
@@ -1,5 +1,5 @@
1
- import { c as createEvents, e as extend, u as useMutableCallback, a as createRoot, i as isRef, E as ErrorBoundary, B as Block, b as useIsomorphicLayoutEffect, d as unmountComponentAtNode } from './index-21f75f82.esm.js';
2
- export { t as ReactThreeFiber, v as _roots, s as act, o as addAfterEffect, n as addEffect, p as addTail, m as advance, j as applyProps, f as context, c as createEvents, g as createPortal, a as createRoot, k as dispose, e as extend, q as getRootState, l as invalidate, h as reconciler, r as render, d as unmountComponentAtNode, z as useFrame, A as useGraph, w as useInstanceHandle, C as useLoader, x as useStore, y as useThree } from './index-21f75f82.esm.js';
1
+ import { c as createEvents, e as extend, u as useMutableCallback, a as createRoot, i as isRef, E as ErrorBoundary, B as Block, b as useIsomorphicLayoutEffect, d as unmountComponentAtNode } from './index-6f0019d1.esm.js';
2
+ export { t as ReactThreeFiber, w as _roots, v as act, o as addAfterEffect, n as addEffect, p as addTail, m as advance, j as applyProps, f as context, c as createEvents, g as createPortal, a as createRoot, k as dispose, e as extend, q as flushGlobalEffects, s as getRootState, l as invalidate, h as reconciler, r as render, d as unmountComponentAtNode, A as useFrame, C as useGraph, x as useInstanceHandle, D as useLoader, y as useStore, z as useThree } from './index-6f0019d1.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';
@@ -2,7 +2,7 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var index = require('../../dist/index-f307fe93.cjs.dev.js');
5
+ var index = require('../../dist/index-8539ced2.cjs.dev.js');
6
6
  var _extends = require('@babel/runtime/helpers/extends');
7
7
  var React = require('react');
8
8
  var THREE = require('three');
@@ -402,10 +402,12 @@ exports.addTail = index.addTail;
402
402
  exports.advance = index.advance;
403
403
  exports.applyProps = index.applyProps;
404
404
  exports.context = index.context;
405
+ exports.createEvents = index.createEvents;
405
406
  exports.createPortal = index.createPortal;
406
407
  exports.createRoot = index.createRoot;
407
408
  exports.dispose = index.dispose;
408
409
  exports.extend = index.extend;
410
+ exports.flushGlobalEffects = index.flushGlobalEffects;
409
411
  exports.getRootState = index.getRootState;
410
412
  exports.invalidate = index.invalidate;
411
413
  exports.reconciler = index.reconciler;
@@ -2,7 +2,7 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var index = require('../../dist/index-bd4f185a.cjs.prod.js');
5
+ var index = require('../../dist/index-57cb2f85.cjs.prod.js');
6
6
  var _extends = require('@babel/runtime/helpers/extends');
7
7
  var React = require('react');
8
8
  var THREE = require('three');
@@ -402,10 +402,12 @@ exports.addTail = index.addTail;
402
402
  exports.advance = index.advance;
403
403
  exports.applyProps = index.applyProps;
404
404
  exports.context = index.context;
405
+ exports.createEvents = index.createEvents;
405
406
  exports.createPortal = index.createPortal;
406
407
  exports.createRoot = index.createRoot;
407
408
  exports.dispose = index.dispose;
408
409
  exports.extend = index.extend;
410
+ exports.flushGlobalEffects = index.flushGlobalEffects;
409
411
  exports.getRootState = index.getRootState;
410
412
  exports.invalidate = index.invalidate;
411
413
  exports.reconciler = index.reconciler;
@@ -1,5 +1,5 @@
1
- import { c as createEvents, e as extend, u as useMutableCallback, a as createRoot, E as ErrorBoundary, B as Block, d as unmountComponentAtNode } from '../../dist/index-21f75f82.esm.js';
2
- export { t as ReactThreeFiber, v as _roots, s as act, o as addAfterEffect, n as addEffect, p as addTail, m as advance, j as applyProps, f as context, g as createPortal, a as createRoot, k as dispose, e as extend, q as getRootState, l as invalidate, h as reconciler, r as render, d as unmountComponentAtNode, z as useFrame, A as useGraph, w as useInstanceHandle, C as useLoader, x as useStore, y as useThree } from '../../dist/index-21f75f82.esm.js';
1
+ import { c as createEvents, e as extend, u as useMutableCallback, a as createRoot, E as ErrorBoundary, B as Block, d as unmountComponentAtNode } from '../../dist/index-6f0019d1.esm.js';
2
+ export { t as ReactThreeFiber, w as _roots, v as act, o as addAfterEffect, n as addEffect, p as addTail, m as advance, j as applyProps, f as context, c as createEvents, g as createPortal, a as createRoot, k as dispose, e as extend, q as flushGlobalEffects, s as getRootState, l as invalidate, h as reconciler, r as render, d as unmountComponentAtNode, A as useFrame, C as useGraph, x as useInstanceHandle, D as useLoader, y as useStore, z as useThree } from '../../dist/index-6f0019d1.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';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-three/fiber",
3
- "version": "8.8.10",
3
+ "version": "8.9.0",
4
4
  "description": "A React renderer for Threejs",
5
5
  "keywords": [
6
6
  "react",
package/readme.md CHANGED
@@ -10,112 +10,160 @@
10
10
 
11
11
  react-three-fiber is a <a href="https://reactjs.org/docs/codebase-overview.html#renderers">React renderer</a> for threejs.
12
12
 
13
+ Build your scene declaratively with re-usable, self-contained components that react to state, are readily interactive and can participate in React's ecosystem.
14
+
13
15
  ```bash
14
16
  npm install three @react-three/fiber
15
17
  ```
16
18
 
17
- ### Why?
18
-
19
- Build your scene declaratively with re-usable, self-contained components that react to state, are readily interactive and can tap into React's ecosystem.
20
-
21
19
  #### Does it have limitations?
22
20
 
23
- None. Everything that works in threejs will work here without exception.
21
+ None. Everything that works in Threejs will work here without exception.
24
22
 
25
- #### Can it keep up with frequent updates to threejs?
23
+ #### Is it slower than plain Threejs?
26
24
 
27
- Yes, because it merely expresses threejs in JSX: `<mesh />` becomes `new THREE.Mesh()`, and that happens dynamically. There is no hard dependency on a particular threejs version, it does not wrap or duplicate a single threejs class.
25
+ No. There is no overhead. Components render outside of React. It outperforms Threejs in scale due to Reacts scheduling abilities.
28
26
 
29
- #### Is it slower than plain threejs?
27
+ #### Can it keep up with frequent feature updates to Threejs?
30
28
 
31
- There is no additional overhead. Components participate in the renderloop outside of React.
29
+ Yes. It merely expresses Threejs in JSX: `<mesh />` becomes `new THREE.Mesh()`, and that happens dynamically. If a new Threejs version adds, removes or changes features, it will be available to you instantly without depending on updates to this library.
32
30
 
33
31
  ### What does it look like?
34
32
 
35
33
  <table>
36
- <tr>
37
- <td>Let's make a re-usable component that has its own state, reacts to user-input and participates in the render-loop. (<a href="https://codesandbox.io/s/rrppl0y8l4?file=/src/App.js">live demo</a>).</td>
38
- <td>
39
- <a href="https://codesandbox.io/s/rrppl0y8l4">
40
- <img src="https://i.imgur.com/sS4ArrZ.gif" /></td>
41
- </a>
42
- </tr>
34
+ <tbody>
35
+ <tr>
36
+ <td>Let's make a re-usable component that has its own state, reacts to user-input and participates in the render-loop. (<a href="https://codesandbox.io/s/rrppl0y8l4?file=/src/App.js">live demo</a>).</td>
37
+ <td>
38
+ <a href="https://codesandbox.io/s/rrppl0y8l4">
39
+ <img src="https://i.imgur.com/sS4ArrZ.gif" />
40
+ </a>
41
+ </td>
42
+ </tr>
43
+ </tbody>
43
44
  </table>
44
45
 
45
- #### Imports first
46
-
47
46
  ```jsx
47
+ import { createRoot } from 'react-dom/client'
48
48
  import React, { useRef, useState } from 'react'
49
- import ReactDOM from 'react-dom'
50
49
  import { Canvas, useFrame } from '@react-three/fiber'
51
- ```
52
50
 
53
- #### Define a component
54
-
55
- ```jsx
56
51
  function Box(props) {
57
- // This reference will give us direct access to the mesh
58
- const mesh = useRef()
59
- // Set up state for the hovered and active state
60
- const [hovered, setHover] = useState(false)
61
- const [active, setActive] = useState(false)
62
- // Rotate mesh every frame, this is outside of React without overhead
63
- useFrame(() => (mesh.current.rotation.x += 0.01))
64
-
52
+ // This reference gives us direct access to the THREE.Mesh object
53
+ const ref = useRef()
54
+ // Hold state for hovered and clicked events
55
+ const [hovered, hover] = useState(false)
56
+ const [clicked, click] = useState(false)
57
+ // Subscribe this component to the render-loop, rotate the mesh every frame
58
+ useFrame((state, delta) => (ref.current.rotation.x += 0.01))
59
+ // Return the view, these are regular Threejs elements expressed in JSX
65
60
  return (
66
61
  <mesh
67
62
  {...props}
68
- ref={mesh}
69
- scale={active ? 1.5 : 1}
70
- onClick={(event) => setActive(!active)}
71
- onPointerOver={(event) => setHover(true)}
72
- onPointerOut={(event) => setHover(false)}>
73
- <boxGeometry args={[1, 2, 3]} />
63
+ ref={ref}
64
+ scale={clicked ? 1.5 : 1}
65
+ onClick={(event) => click(!clicked)}
66
+ onPointerOver={(event) => hover(true)}
67
+ onPointerOut={(event) => hover(false)}>
68
+ <boxGeometry args={[1, 1, 1]} />
74
69
  <meshStandardMaterial color={hovered ? 'hotpink' : 'orange'} />
75
70
  </mesh>
76
71
  )
77
72
  }
73
+
74
+ createRoot(document.getElementById('root')).render(
75
+ <Canvas>
76
+ <ambientLight />
77
+ <pointLight position={[10, 10, 10]} />
78
+ <Box position={[-1.2, 0, 0]} />
79
+ <Box position={[1.2, 0, 0]} />
80
+ </Canvas>,
81
+ )
78
82
  ```
79
83
 
80
- #### Compose the scene
84
+ <details>
85
+ <summary>Show TypeScript example</summary>
86
+
87
+ ```bash
88
+ npm install @types/three
89
+ ```
81
90
 
82
- Either use `Canvas`, which you can think of as a portal to threejs inside your regular dom graph. Everything within it is a [native threejs element](https://threejs.org/docs). If you want to mix Webgl and Html (react-dom) this is what you should use.
91
+ ```tsx
92
+ import * as THREE from 'three'
93
+ import { createRoot } from 'react-dom/client'
94
+ import React, { useRef, useState } from 'react'
95
+ import { Canvas, useFrame, ThreeElements } from '@react-three/fiber'
83
96
 
84
- ```jsx
85
- ReactDOM.render(
97
+ function Box(props: ThreeElements['mesh']) {
98
+ const ref = useRef<THREE.Mesh>(null!)
99
+ const [hovered, hover] = useState(false)
100
+ const [clicked, click] = useState(false)
101
+ useFrame((state, delta) => (ref.current.rotation.x += 0.01))
102
+ return (
103
+ <mesh
104
+ {...props}
105
+ ref={ref}
106
+ scale={clicked ? 1.5 : 1}
107
+ onClick={(event) => click(!clicked)}
108
+ onPointerOver={(event) => hover(true)}
109
+ onPointerOut={(event) => hover(false)}>
110
+ <boxGeometry args={[1, 1, 1]} />
111
+ <meshStandardMaterial color={hovered ? 'hotpink' : 'orange'} />
112
+ </mesh>
113
+ )
114
+ }
115
+
116
+ createRoot(document.getElementById('root') as HTMLElement).render(
86
117
  <Canvas>
87
118
  <ambientLight />
88
119
  <pointLight position={[10, 10, 10]} />
89
120
  <Box position={[-1.2, 0, 0]} />
90
121
  <Box position={[1.2, 0, 0]} />
91
122
  </Canvas>,
92
- document.getElementById('root'),
93
123
  )
94
124
  ```
95
125
 
96
- Or use react-three-fibers own `render` function, which is a little more low-level but could save you the extra cost of carrying react-dom. It renders into a dom `canvas` element. Use this for Webgl-only apps.
126
+ Live demo: https://codesandbox.io/s/icy-tree-brnsm?file=/src/App.tsx
97
127
 
98
- ```jsx
99
- import { render } from '@react-three/fiber'
128
+ </details>
129
+
130
+ <details>
131
+ <summary>Show React Native example</summary>
132
+
133
+ This example relies on react 18 and uses `expo-cli`, but you can create a bare project with their template or with the `react-native` CLI.
100
134
 
101
- render(<Scene />, document.querySelector('canvas'))
135
+ ```bash
136
+ # Install expo-cli, this will create our app
137
+ npm install expo-cli -g
138
+ # Create app and cd into it
139
+ expo init my-app
140
+ cd my-app
141
+ # Install dependencies
142
+ npm install three @react-three/fiber@beta react@rc
143
+ # Start
144
+ expo start
102
145
  ```
103
146
 
104
- <details>
105
- <summary>Show TypeScript example</summary>
147
+ Some configuration may be required to tell the Metro bundler about your assets if you use `useLoader` or Drei abstractions like `useGLTF` and `useTexture`:
106
148
 
107
- ```tsx
108
- import { Canvas, MeshProps, useFrame } from '@react-three/fiber'
149
+ ```js
150
+ // metro.config.js
151
+ module.exports = {
152
+ resolver: {
153
+ sourceExts: ['js', 'jsx', 'json', 'ts', 'tsx', 'cjs'],
154
+ assetExts: ['glb', 'png', 'jpg'],
155
+ },
156
+ }
157
+ ```
109
158
 
110
- const Box: React.FC<MeshProps> = (props) => {
111
- // This reference will give us direct access to the mesh
112
- const mesh = useRef<THREE.Mesh>(null!)
113
- // Set up state for the hovered and active state
159
+ ```tsx
160
+ import React, { useRef, useState } from 'react'
161
+ import { Canvas, useFrame } from '@react-three/fiber/native'
162
+ function Box(props) {
163
+ const mesh = useRef(null)
114
164
  const [hovered, setHover] = useState(false)
115
165
  const [active, setActive] = useState(false)
116
- // Rotate mesh every frame, this is outside of React without overhead
117
- useFrame(() => (mesh.current.rotation.x += 0.01))
118
-
166
+ useFrame((state, delta) => (mesh.current.rotation.x += 0.01))
119
167
  return (
120
168
  <mesh
121
169
  {...props}
@@ -124,32 +172,32 @@ const Box: React.FC<MeshProps> = (props) => {
124
172
  onClick={(event) => setActive(!active)}
125
173
  onPointerOver={(event) => setHover(true)}
126
174
  onPointerOut={(event) => setHover(false)}>
127
- <boxGeometry args={[1, 2, 3]} />
175
+ <boxGeometry args={[1, 1, 1]} />
128
176
  <meshStandardMaterial color={hovered ? 'hotpink' : 'orange'} />
129
177
  </mesh>
130
178
  )
131
179
  }
132
-
133
- ReactDOM.render(
134
- <Canvas>
135
- <ambientLight />
136
- <pointLight position={[10, 10, 10]} />
137
- <Box position={[-1.2, 0, 0]} />
138
- <Box position={[1.2, 0, 0]} />
139
- </Canvas>,
140
- document.getElementById('root'),
141
- )
180
+ export default function App() {
181
+ return (
182
+ <Canvas>
183
+ <ambientLight />
184
+ <pointLight position={[10, 10, 10]} />
185
+ <Box position={[-1.2, 0, 0]} />
186
+ <Box position={[1.2, 0, 0]} />
187
+ </Canvas>
188
+ )
189
+ }
142
190
  ```
143
191
 
144
192
  </details>
145
193
 
146
194
  ---
147
195
 
148
- # Documentation
196
+ # Documentation, tutorials, examples
197
+
198
+ Visit [docs.pmnd.rs](https://docs.pmnd.rs/react-three-fiber)
149
199
 
150
- - [api.md](/markdown/api.md)
151
- - [pitfalls.md](/markdown/pitfalls.md)
152
- - [testing.md](/packages/test-renderer)
200
+ <a href="https://docs.pmnd.rs/react-three-fiber"><img src="/docs/preview.jpg"></a>
153
201
 
154
202
  # Fundamentals
155
203
 
@@ -158,13 +206,14 @@ You need to be versed in both React and Threejs before rushing into this. If you
158
206
  1. Make sure you have a [basic grasp of Threejs](https://threejs.org/docs/index.html#manual/en/introduction/Creating-a-scene). Keep that site open.
159
207
  2. When you know what a scene is, a camera, mesh, geometry, material, fork the [demo above](https://github.com/pmndrs/react-three-fiber#what-does-it-look-like).
160
208
  3. [Look up](https://threejs.org/docs/index.html#api/en/objects/Mesh) the JSX elements that you see (mesh, ambientLight, etc), _all_ threejs exports are native to three-fiber.
161
- 4. Try changing some values, scroll though our [Api](/markdown/api.md) to see what the various settings and hooks do.
209
+ 4. Try changing some values, scroll through our [API](https://docs.pmnd.rs/react-three-fiber/API) to see what the various settings and hooks do.
162
210
 
163
211
  Some reading material:
164
212
 
165
213
  - [Threejs-docs](https://threejs.org/docs)
166
214
  - [Threejs-examples](https://threejs.org/examples)
167
215
  - [Threejs-fundamentals](https://threejs.org/manual/#en/fundamentals)
216
+ - [three.js-journey](https://threejs-journey.com)
168
217
  - [Discover Threejs](https://discoverthreejs.com)
169
218
  - [Do's and don'ts](https://discoverthreejs.com/tips-and-tricks) for performance and best practices
170
219
  - [react-three-fiber alligator.io tutorial](https://alligator.io/react/react-with-threejs) by [@dghez\_](https://twitter.com/dghez_)
@@ -177,9 +226,11 @@ Some reading material:
177
226
  - [`@react-three/flex`](https://github.com/pmndrs/react-three-flex) &ndash; flexbox for react-three-fiber
178
227
  - [`@react-three/xr`](https://github.com/pmndrs/react-xr) &ndash; VR/AR controllers and events
179
228
  - [`@react-three/cannon`](https://github.com/pmndrs/use-cannon) &ndash; physics based hooks
229
+ - [`@react-three/a11y`](https://github.com/pmndrs/react-three-a11y) &ndash; real a11y for your scene
180
230
  - [`zustand`](https://github.com/pmndrs/zustand) &ndash; state management
181
231
  - [`react-spring`](https://github.com/pmndrs/react-spring) &ndash; a spring-physics-based animation library
182
232
  - [`react-use-gesture`](https://github.com/pmndrs/react-use-gesture) &ndash; mouse/touch gestures
233
+ - [`leva`](https://github.com/pmndrs/leva) &ndash; create GUI controls in seconds
183
234
 
184
235
  # How to contribute
185
236