77 * @flow strict-local
88 */
99
10+ import type { CallbackRef } from '../../types/react' ;
11+
1012import * as React from 'react' ;
1113
1214import { errorMsg } from '../../shared/logUtils' ;
15+ import { useElementCallback } from '../../shared/useElementCallback' ;
1316import { useViewportScale } from './ContextViewportScale' ;
1417
1518type Options = {
@@ -25,9 +28,8 @@ function errorUnimplemented(name: string) {
2528 }
2629}
2730
28- function proxy ( nativeRef : React . RefObject < Node > , name : string ) {
31+ function proxy ( node : Node , name : string ) {
2932 return ( ...args : Array < mixed > ) => {
30- const node = nativeRef . current ;
3133 if ( node ?. [ name ] ) {
3234 return node [ name ] ( ...args ) ;
3335 }
@@ -38,68 +40,89 @@ function proxy(nativeRef: React.RefObject<Node>, name: string) {
3840export function useStrictDOMElement < T > (
3941 ref : React . RefSetter < Node > ,
4042 { tagName } : Options
41- ) : React . RefObject < T | null > {
42- const nativeRef = React . useRef < T | null > ( null ) ;
43+ ) : CallbackRef < T > {
4344 const { scale : viewportScale } = useViewportScale ( ) ;
4445
45- React . useImperativeHandle ( ref , ( ) => {
46- return {
47- get __nativeTag ( ) {
48- const node = nativeRef . current as Node ;
49- return node ?. __nativeTag ;
50- } ,
51- addEventListener : proxy ( nativeRef , 'addEventListener' ) ,
52- animate : proxy ( nativeRef , 'animate' ) ,
53- blur : proxy ( nativeRef , 'blur' ) ,
54- click : proxy ( nativeRef , 'click' ) ,
55- get complete ( ) {
56- if ( tagName === 'img' ) {
57- const node = nativeRef . current as Node ;
58- if ( node ?. complete == null ) {
59- // Assume images are never pre-loaded in React Native
60- return false ;
46+ const elementCallback = useElementCallback (
47+ React . useCallback (
48+ // $FlowFixMe[unclear-type]
49+ ( node : any ) => {
50+ const obj = {
51+ get __nativeTag ( ) {
52+ return node ?. __nativeTag ;
53+ } ,
54+ addEventListener : proxy ( node , 'addEventListener' ) ,
55+ animate : proxy ( node , 'animate' ) ,
56+ blur : proxy ( node , 'blur' ) ,
57+ click : proxy ( node , 'click' ) ,
58+ get complete ( ) {
59+ if ( tagName === 'img' ) {
60+ if ( node ?. complete == null ) {
61+ // Assume images are never pre-loaded in React Native
62+ return false ;
63+ } else {
64+ return node . complete ;
65+ }
66+ }
67+ } ,
68+ contains : proxy ( node , 'contains' ) ,
69+ dispatchEvent : proxy ( node , 'dispatchEvent' ) ,
70+ focus : proxy ( node , 'focus' ) ,
71+ getAttribute : proxy ( node , 'getAttribute' ) ,
72+ getBoundingClientRect ( ) {
73+ const getBoundingClientRect =
74+ node ?. getBoundingClientRect ??
75+ node ?. unstable_getBoundingClientRect ;
76+ if ( getBoundingClientRect ) {
77+ const rect = getBoundingClientRect . call ( node ) ;
78+ if ( viewportScale !== 1 ) {
79+ return new DOMRect (
80+ rect . x / viewportScale ,
81+ rect . y / viewportScale ,
82+ rect . width / viewportScale ,
83+ rect . height / viewportScale
84+ ) ;
85+ }
86+ return rect ;
87+ }
88+ return errorUnimplemented ( 'getBoundingClientRect' ) ;
89+ } ,
90+ getRootNode : proxy ( node , 'getRootNode' ) ,
91+ hasPointerCapture : proxy ( node , 'hasPointerCapture' ) ,
92+ nodeName : tagName . toUpperCase ( ) ,
93+ releasePointerCapture : proxy ( node , 'releasePointerCapture' ) ,
94+ removeEventListener : proxy ( node , 'removeEventListener' ) ,
95+ scroll : proxy ( node , 'scroll' ) ,
96+ scrollBy : proxy ( node , 'scrollBy' ) ,
97+ scrollIntoView : proxy ( node , 'scrollIntoView' ) ,
98+ scrollTo : proxy ( node , 'scrollTo' ) ,
99+ select : proxy ( node , 'select' ) ,
100+ setSelectionRange : proxy ( node , 'setSelectionRange' ) ,
101+ setPointerCapture : proxy ( node , 'setPointerCapture' ) ,
102+ showPicker : proxy ( node , 'showPicker' )
103+ } ;
104+ if ( ref == null ) {
105+ return undefined ;
106+ } else {
107+ if ( typeof ref === 'function' ) {
108+ // $FlowFixMe[incompatible-type] - Flow does not understand ref cleanup.
109+ const cleanup : void | ( ( ) => void ) = ref ( obj ) ;
110+ return typeof cleanup === 'function'
111+ ? cleanup
112+ : ( ) => {
113+ ref ( null ) ;
114+ } ;
61115 } else {
62- return node . complete ;
63- }
64- }
65- } ,
66- contains : proxy ( nativeRef , 'contains' ) ,
67- dispatchEvent : proxy ( nativeRef , 'dispatchEvent' ) ,
68- focus : proxy ( nativeRef , 'focus' ) ,
69- getAttribute : proxy ( nativeRef , 'getAttribute' ) ,
70- getBoundingClientRect ( ) {
71- const node = nativeRef . current as Node ;
72- const getBoundingClientRect =
73- node ?. getBoundingClientRect ?? node ?. unstable_getBoundingClientRect ;
74- if ( getBoundingClientRect ) {
75- const rect = getBoundingClientRect . call ( node ) ;
76- if ( viewportScale !== 1 ) {
77- return new DOMRect (
78- rect . x / viewportScale ,
79- rect . y / viewportScale ,
80- rect . width / viewportScale ,
81- rect . height / viewportScale
82- ) ;
116+ ref . current = obj ;
117+ return ( ) => {
118+ ref . current = null ;
119+ } ;
83120 }
84- return rect ;
85121 }
86- return errorUnimplemented ( 'getBoundingClientRect' ) ;
87122 } ,
88- getRootNode : proxy ( nativeRef , 'getRootNode' ) ,
89- hasPointerCapture : proxy ( nativeRef , 'hasPointerCapture' ) ,
90- nodeName : tagName . toUpperCase ( ) ,
91- releasePointerCapture : proxy ( nativeRef , 'releasePointerCapture' ) ,
92- removeEventListener : proxy ( nativeRef , 'removeEventListener' ) ,
93- scroll : proxy ( nativeRef , 'scroll' ) ,
94- scrollBy : proxy ( nativeRef , 'scrollBy' ) ,
95- scrollIntoView : proxy ( nativeRef , 'scrollIntoView' ) ,
96- scrollTo : proxy ( nativeRef , 'scrollTo' ) ,
97- select : proxy ( nativeRef , 'select' ) ,
98- setSelectionRange : proxy ( nativeRef , 'setSelectionRange' ) ,
99- setPointerCapture : proxy ( nativeRef , 'setPointerCapture' ) ,
100- showPicker : proxy ( nativeRef , 'showPicker' )
101- } ;
102- } , [ tagName , viewportScale ] ) ;
123+ [ ref , tagName , viewportScale ]
124+ )
125+ ) ;
103126
104- return nativeRef ;
127+ return elementCallback ;
105128}
0 commit comments