Skip to content

Commit cbf5c2f

Browse files
committed
feat: configure behavior on text instance
1 parent 4157481 commit cbf5c2f

File tree

3 files changed

+53
-16
lines changed

3 files changed

+53
-16
lines changed

packages/fiber/src/core/index.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,14 @@ export type RenderProps<TCanvas extends Canvas> = {
104104
onCreated?: (state: RootState) => void
105105
/** Response for pointer clicks that have missed any target */
106106
onPointerMissed?: (event: MouseEvent) => void
107+
108+
/**
109+
* Controls how React Three Fiber handles text instances.
110+
* - 'noop': No action is taken.
111+
* - 'warn': Logs a warning when a text instance is detected. (default)
112+
* - 'throw': Throws an error when a text instance is detected.
113+
*/
114+
onTextInstance?: 'noop' | 'warn' | 'throw'
107115
}
108116

109117
const createRendererInstance = <TCanvas extends Canvas>(gl: GLProps, canvas: TCanvas): THREE.WebGLRenderer => {
@@ -197,6 +205,7 @@ function createRoot<TCanvas extends Canvas>(canvas: TCanvas): ReconcilerRoot<TCa
197205
raycaster: raycastOptions,
198206
camera: cameraOptions,
199207
onPointerMissed,
208+
onTextInstance = 'warn',
200209
} = props
201210

202211
let state = store.getState()
@@ -370,6 +379,8 @@ function createRoot<TCanvas extends Canvas>(canvas: TCanvas): ReconcilerRoot<TCa
370379
// Check performance
371380
if (performance && !is.equ(performance, state.performance, shallowLoose))
372381
state.set((state) => ({ performance: { ...state.performance, ...performance } }))
382+
// Check onTextInstance
383+
if (state.onTextInstance !== onTextInstance) state.set({ onTextInstance })
373384

374385
// Set locals
375386
onCreated = onCreatedCallback

packages/fiber/src/core/renderer.ts

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
1-
import * as THREE from 'three'
2-
import { UseBoundStore } from 'zustand'
31
import Reconciler from 'react-reconciler'
4-
import { unstable_IdlePriority as idlePriority, unstable_scheduleCallback as scheduleCallback } from 'scheduler'
52
import { DefaultEventPriority } from 'react-reconciler/constants'
3+
import { unstable_IdlePriority as idlePriority, unstable_scheduleCallback as scheduleCallback } from 'scheduler'
4+
import * as THREE from 'three'
5+
import { UseBoundStore } from 'zustand'
6+
import { EventHandlers, removeInteractivity } from './events'
7+
import { RootState } from './store'
68
import {
7-
is,
8-
prepare,
9-
findInitialRoot,
10-
diffProps,
119
DiffSet,
1210
applyProps,
13-
updateInstance,
14-
invalidateInstance,
1511
attach,
1612
detach,
13+
diffProps,
14+
findInitialRoot,
15+
invalidateInstance,
16+
is,
17+
prepare,
18+
updateInstance,
1719
} from './utils'
18-
import { RootState } from './store'
19-
import { EventHandlers, removeInteractivity } from './events'
2020

2121
export type Root = { fiber: Reconciler.FiberRoot; store: UseBoundStore<RootState> }
2222

@@ -85,7 +85,19 @@ interface Catalogue {
8585
export const catalogue: Catalogue = {}
8686
const extend = (objects: object): void => void Object.assign(catalogue, objects)
8787

88-
function createRenderer<TCanvas>(_roots: Map<TCanvas, Root>, _getEventPriority?: () => any) {
88+
type OnTextInstanceOption = 'noop' | 'warn' | 'throw'
89+
90+
interface RendererOptions {
91+
onTextInstance?: OnTextInstanceOption
92+
}
93+
94+
function createRenderer<TCanvas>(
95+
_roots: Map<TCanvas, Root>,
96+
_getEventPriority?: () => any,
97+
options: RendererOptions = {},
98+
) {
99+
const { onTextInstance = 'warn' } = options
100+
89101
function createInstance(
90102
type: string,
91103
{ args = [], attach, ...props }: InstanceProps,
@@ -301,9 +313,20 @@ function createRenderer<TCanvas>(_roots: Map<TCanvas, Root>, _getEventPriority?:
301313
})
302314
}
303315

304-
// Don't handle text instances, warn on undefined behavior
305-
const handleTextInstance = () =>
306-
console.warn('Text is not allowed in the R3F tree! This could be stray whitespace or characters.')
316+
const handleTextInstance = (text: string) => {
317+
switch (onTextInstance) {
318+
case 'noop': {
319+
break
320+
}
321+
case 'throw': {
322+
throw new Error(`Text is not allowed in the R3F tree! This could be stray whitespace or characters: "${text}"`)
323+
}
324+
case 'warn':
325+
default: {
326+
console.warn(`Text is not allowed in the R3F tree! This could be stray whitespace or characters: "${text}"`)
327+
}
328+
}
329+
}
307330

308331
const reconciler = Reconciler<
309332
HostConfig['type'],
@@ -445,4 +468,4 @@ function createRenderer<TCanvas>(_roots: Map<TCanvas, Root>, _getEventPriority?:
445468
return { reconciler, applyProps }
446469
}
447470

448-
export { prepare, createRenderer, extend }
471+
export { createRenderer, extend, prepare }

packages/fiber/src/core/store.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,8 @@ export type RootState = {
161161
previousRoot?: UseBoundStore<RootState, StoreApi<RootState>>
162162
/** Internals */
163163
internal: InternalState
164+
/** Controls how React Three Fiber handles text instances. */
165+
onTextInstance: 'noop' | 'warn' | 'throw'
164166
}
165167

166168
const context = React.createContext<UseBoundStore<RootState>>(null!)
@@ -221,6 +223,7 @@ const createStore = (invalidate: Invalidate, advance: Advance): UseBoundStore<Ro
221223
mouse: pointer,
222224

223225
frameloop: 'always',
226+
onTextInstance: 'warn',
224227
onPointerMissed: undefined,
225228

226229
performance: {

0 commit comments

Comments
 (0)