Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[v9] chore: replace forwardRef with props.ref #3282

Open
wants to merge 1 commit into
base: v9
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 16 additions & 18 deletions packages/fiber/__mocks__/react-native.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,25 @@
import * as React from 'react'
import { ViewProps, LayoutChangeEvent } from 'react-native'
import { ViewProps, LayoutChangeEvent, View as RNView } from 'react-native'

// Mocks a View or container as React sees it
const Container = React.memo(
React.forwardRef(({ onLayout, ...props }: ViewProps, ref) => {
React.useLayoutEffect(() => {
onLayout?.({
nativeEvent: {
layout: {
x: 0,
y: 0,
width: 1280,
height: 800,
},
const Container = React.memo(({ onLayout, ...props }: ViewProps) => {
React.useLayoutEffect(() => {
onLayout?.({
nativeEvent: {
layout: {
x: 0,
y: 0,
width: 1280,
height: 800,
},
} as LayoutChangeEvent)
}, [onLayout])
},
} as LayoutChangeEvent)
}, [onLayout])

React.useImperativeHandle(ref, () => props)
// React.useImperativeHandle(ref, () => props)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Idk what to do with this? It didn't work before either


return null
}),
)
return null
})

export const View = Container
export const Pressable = Container
Expand Down
245 changes: 121 additions & 124 deletions packages/fiber/src/native/Canvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const _View = View as any
export interface CanvasProps extends Omit<RenderProps<HTMLCanvasElement>, 'size' | 'dpr'>, Omit<ViewProps, 'children'> {
children: React.ReactNode
style?: ViewStyle
ref?: React.Ref<View>
}

export interface Props extends CanvasProps {}
Expand All @@ -22,13 +23,81 @@ export interface Props extends CanvasProps {}
* A native canvas which accepts threejs elements as children.
* @see https://docs.pmnd.rs/react-three-fiber/api/canvas
*/
const CanvasImpl = /*#__PURE__*/ React.forwardRef<View, Props>(
(
{
children,
style,
function CanvasImpl({
ref,
children,
style,
gl,
events = createTouchEvents,
shadows,
linear,
flat,
legacy,
orthographic,
frameloop,
performance,
raycaster,
camera,
scene,
onPointerMissed,
onCreated,
...props
}: Props) {
// Create a known catalogue of Threejs-native elements
// This will include the entire THREE namespace by default, users can extend
// their own elements by using the createRoot API instead
React.useMemo(() => extend(THREE as any), [])

const Bridge = useBridge()

const [{ width, height, top, left }, setSize] = React.useState<Size>({ width: 0, height: 0, top: 0, left: 0 })
const [canvas, setCanvas] = React.useState<HTMLCanvasElement | null>(null)
const [bind, setBind] = React.useState<any>()
React.useImperativeHandle(ref, () => viewRef.current)

const handlePointerMissed = useMutableCallback(onPointerMissed)
const [block, setBlock] = React.useState<SetBlock>(false)
const [error, setError] = React.useState<Error | undefined>(undefined)

// Suspend this component if block is a promise (2nd run)
if (block) throw block
// Throw exception outwards if anything within canvas throws
if (error) throw error

const viewRef = React.useRef<View>(null!)
const root = React.useRef<ReconcilerRoot<HTMLCanvasElement>>(null!)

const [antialias, setAntialias] = React.useState<boolean>(true)

const onLayout = React.useCallback((e: LayoutChangeEvent) => {
const { width, height, x, y } = e.nativeEvent.layout
setSize({ width, height, top: y, left: x })
}, [])

// Called on context create or swap
// https://github.com/pmndrs/react-three-fiber/pull/2297
const onContextCreate = React.useCallback((context: ExpoWebGLRenderingContext) => {
const canvasShim = {
width: context.drawingBufferWidth,
height: context.drawingBufferHeight,
style: {},
addEventListener: (() => {}) as any,
removeEventListener: (() => {}) as any,
clientHeight: context.drawingBufferHeight,
getContext: ((_: any, { antialias = false }) => {
setAntialias(antialias)
return context
}) as any,
} as HTMLCanvasElement

root.current = createRoot<HTMLCanvasElement>(canvasShim)
setCanvas(canvasShim)
}, [])

if (root.current && width > 0 && height > 0) {
root.current.configure({
gl,
events = createTouchEvents,
events,
shadows,
linear,
flat,
Expand All @@ -39,132 +108,60 @@ const CanvasImpl = /*#__PURE__*/ React.forwardRef<View, Props>(
raycaster,
camera,
scene,
onPointerMissed,
onCreated,
...props
},
forwardedRef,
) => {
// Create a known catalogue of Threejs-native elements
// This will include the entire THREE namespace by default, users can extend
// their own elements by using the createRoot API instead
React.useMemo(() => extend(THREE as any), [])

const Bridge = useBridge()

const [{ width, height, top, left }, setSize] = React.useState<Size>({ width: 0, height: 0, top: 0, left: 0 })
const [canvas, setCanvas] = React.useState<HTMLCanvasElement | null>(null)
const [bind, setBind] = React.useState<any>()
React.useImperativeHandle(forwardedRef, () => viewRef.current)

const handlePointerMissed = useMutableCallback(onPointerMissed)
const [block, setBlock] = React.useState<SetBlock>(false)
const [error, setError] = React.useState<Error | undefined>(undefined)

// Suspend this component if block is a promise (2nd run)
if (block) throw block
// Throw exception outwards if anything within canvas throws
if (error) throw error

const viewRef = React.useRef<View>(null!)
const root = React.useRef<ReconcilerRoot<HTMLCanvasElement>>(null!)

const [antialias, setAntialias] = React.useState<boolean>(true)

const onLayout = React.useCallback((e: LayoutChangeEvent) => {
const { width, height, x, y } = e.nativeEvent.layout
setSize({ width, height, top: y, left: x })
}, [])

// Called on context create or swap
// https://github.com/pmndrs/react-three-fiber/pull/2297
const onContextCreate = React.useCallback((context: ExpoWebGLRenderingContext) => {
const canvasShim = {
width: context.drawingBufferWidth,
height: context.drawingBufferHeight,
style: {},
addEventListener: (() => {}) as any,
removeEventListener: (() => {}) as any,
clientHeight: context.drawingBufferHeight,
getContext: ((_: any, { antialias = false }) => {
setAntialias(antialias)
return context
}) as any,
} as HTMLCanvasElement

root.current = createRoot<HTMLCanvasElement>(canvasShim)
setCanvas(canvasShim)
}, [])

if (root.current && width > 0 && height > 0) {
root.current.configure({
gl,
events,
shadows,
linear,
flat,
legacy,
orthographic,
frameloop,
performance,
raycaster,
camera,
scene,
// expo-gl can only render at native dpr/resolution
// https://github.com/expo/expo-three/issues/39
dpr: PixelRatio.get(),
size: { width, height, top, left },
// Pass mutable reference to onPointerMissed so it's free to update
onPointerMissed: (...args) => handlePointerMissed.current?.(...args),
// Overwrite onCreated to apply RN bindings
onCreated: (state: RootState) => {
// Bind events after creation
setBind(state.events.handlers)

// Bind render to RN bridge
const context = state.gl.getContext() as ExpoWebGLRenderingContext
const renderFrame = state.gl.render.bind(state.gl)
state.gl.render = (scene: THREE.Scene, camera: THREE.Camera) => {
renderFrame(scene, camera)
context.endFrameEXP()
}

return onCreated?.(state)
},
})
root.current.render(
<Bridge>
<ErrorBoundary set={setError}>
<React.Suspense fallback={<Block set={setBlock} />}>{children}</React.Suspense>
</ErrorBoundary>
</Bridge>,
)
// expo-gl can only render at native dpr/resolution
// https://github.com/expo/expo-three/issues/39
dpr: PixelRatio.get(),
size: { width, height, top, left },
// Pass mutable reference to onPointerMissed so it's free to update
onPointerMissed: (...args) => handlePointerMissed.current?.(...args),
// Overwrite onCreated to apply RN bindings
onCreated: (state: RootState) => {
// Bind events after creation
setBind(state.events.handlers)

// Bind render to RN bridge
const context = state.gl.getContext() as ExpoWebGLRenderingContext
const renderFrame = state.gl.render.bind(state.gl)
state.gl.render = (scene: THREE.Scene, camera: THREE.Camera) => {
renderFrame(scene, camera)
context.endFrameEXP()
}

return onCreated?.(state)
},
})
root.current.render(
<Bridge>
<ErrorBoundary set={setError}>
<React.Suspense fallback={<Block set={setBlock} />}>{children}</React.Suspense>
</ErrorBoundary>
</Bridge>,
)
}

React.useEffect(() => {
if (canvas) {
return () => unmountComponentAtNode(canvas!)
}
}, [canvas])

React.useEffect(() => {
if (canvas) {
return () => unmountComponentAtNode(canvas!)
}
}, [canvas])

return (
<_View {...props} ref={viewRef} onLayout={onLayout} style={{ flex: 1, ...style }} {...bind}>
{width > 0 && (
<GLView msaaSamples={antialias ? 4 : 0} onContextCreate={onContextCreate} style={StyleSheet.absoluteFill} />
)}
</_View>
)
},
)
return (
<_View {...props} ref={viewRef} onLayout={onLayout} style={{ flex: 1, ...style }} {...bind}>
{width > 0 && (
<GLView msaaSamples={antialias ? 4 : 0} onContextCreate={onContextCreate} style={StyleSheet.absoluteFill} />
)}
</_View>
)
}

/**
* A native canvas which accepts threejs elements as children.
* @see https://docs.pmnd.rs/react-three-fiber/api/canvas
*/
export const Canvas = React.forwardRef<View, CanvasProps>(function CanvasWrapper(props, ref) {
export function Canvas(props: CanvasProps) {
return (
<FiberProvider>
<CanvasImpl {...props} ref={ref} />
<CanvasImpl {...props} />
</FiberProvider>
)
})
}
Loading