@@ -2,7 +2,6 @@ import type { ViewProps, LayoutChangeEvent } from "react-native";
22import { View } from "react-native" ;
33import {
44 forwardRef ,
5- useEffect ,
65 useImperativeHandle ,
76 useRef ,
87 useState ,
@@ -12,6 +11,7 @@ import {
1211import type { RefObject } from "react" ;
1312
1413import WebGPUNativeView from "./WebGPUViewNativeComponent" ;
14+ import { useSignal } from "./useSignal" ;
1515
1616let CONTEXT_COUNTER = 1 ;
1717function generateContextId ( ) {
@@ -60,34 +60,38 @@ interface Size {
6060 height : number ;
6161}
6262
63- const useSizeFabric = ( ref : RefObject < View > ) => {
64- const [ size , setSize ] = useState < null | Size > ( null ) ;
63+ const useSizeFabric = ( ref : RefObject < View > , onSizeChange : ( v : Size ) => void ) => {
64+ const [ sizeSignal ] = useSignal < null | Size > ( null ) ;
6565 useLayoutEffect ( ( ) => {
6666 if ( ! ref . current ) {
6767 throw new Error ( "Canvas ref is null" ) ;
6868 }
6969 ref . current . measureInWindow ( ( _x , _y , width , height ) => {
70- setSize ( { width, height } ) ;
70+ const size : Size = { width, height } ;
71+ sizeSignal . set ( size ) ;
72+ onSizeChange ( size ) ;
7173 } ) ;
7274 } , [ ref ] ) ;
73- return { size , onLayout : undefined } ;
75+ return { sizeSignal , onLayout : undefined } ;
7476} ;
7577
76- const useSizePaper = ( _ref : RefObject < View > ) => {
77- const [ size , setSize ] = useState < null | Size > ( null ) ;
78+ const useSizePaper = ( _ref : RefObject < View > , onSizeChange : ( v : Size ) => void ) => {
79+ const [ sizeSignal ] = useSignal < null | Size > ( null ) ;
7880 const onLayout = useCallback < ( event : LayoutChangeEvent ) => void > (
7981 ( {
8082 nativeEvent : {
8183 layout : { width, height } ,
8284 } ,
8385 } ) => {
84- if ( size === null ) {
85- setSize ( { width, height } ) ;
86+ if ( sizeSignal . get ( ) === null ) {
87+ const size : Size = { width, height } ;
88+ sizeSignal . set ( size ) ;
89+ onSizeChange ( size ) ;
8690 }
8791 } ,
88- [ size ] ,
92+ [ sizeSignal ] ,
8993 ) ;
90- return { size , onLayout } ;
94+ return { sizeSignal , onLayout } ;
9195} ;
9296
9397export const Canvas = forwardRef <
@@ -97,30 +101,33 @@ export const Canvas = forwardRef<
97101 const viewRef = useRef ( null ) ;
98102 const FABRIC = RNWebGPU . fabric ;
99103 const useSize = FABRIC ? useSizeFabric : useSizePaper ;
100- const [ contextId , _ ] = useState ( ( ) => generateContextId ( ) ) ;
101- const cb = useRef < ( ) => void > ( ) ;
102- const { size, onLayout } = useSize ( viewRef ) ;
103- useEffect ( ( ) => {
104- if ( size && cb . current ) {
105- cb . current ( ) ;
106- }
107- } , [ size ] ) ;
104+ const [ contextId , _ ] = useState ( generateContextId ) ;
105+ const whenReadyCallbacks = useRef < ( ( ) => void ) [ ] > ( [ ] ) ;
106+ const onSizeChange = useCallback ( ( ) => {
107+ // The size of the canvas has been computed, meaning we're ready
108+ // to display things on it!
109+ whenReadyCallbacks . current . forEach ( cb => cb ( ) ) ;
110+ whenReadyCallbacks . current = [ ] ;
111+ } , [ ] ) ;
112+ const { sizeSignal, onLayout } = useSize ( viewRef , onSizeChange ) ;
113+
108114 useImperativeHandle ( ref , ( ) => ( {
109115 getContextId : ( ) => contextId ,
110116 getNativeSurface : ( ) => {
111- if ( size === null ) {
117+ if ( sizeSignal . get ( ) === null ) {
112118 throw new Error ( "[WebGPU] Canvas size is not available yet" ) ;
113119 }
114120 return RNWebGPU . getNativeSurface ( contextId ) ;
115121 } ,
116122 whenReady ( callback : ( ) => void ) {
117- if ( size === null ) {
118- cb . current = callback ;
123+ if ( sizeSignal . get ( ) === null ) {
124+ whenReadyCallbacks . current . push ( callback ) ;
119125 } else {
120126 callback ( ) ;
121127 }
122128 } ,
123129 getContext ( contextName : "webgpu" ) : RNCanvasContext | null {
130+ const size = sizeSignal . get ( ) ;
124131 if ( contextName !== "webgpu" ) {
125132 throw new Error ( `[WebGPU] Unsupported context: ${ contextName } ` ) ;
126133 }
0 commit comments