Skip to content

Commit

Permalink
Merge pull request #533 from jpudysz/feature/copy-native-components-p…
Browse files Browse the repository at this point in the history
…roperties

feat: copy native components properties
  • Loading branch information
jpudysz authored Jan 29, 2025
2 parents 406a156 + f7a9cf0 commit d2a232f
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 86 deletions.
5 changes: 4 additions & 1 deletion src/components/native/ImageBackground.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ import type { UnistylesValues } from '../../types'
import { getClassName } from '../../core'
import { isServer } from '../../web/utils'
import { UnistylesShadowRegistry } from '../../web'
import { copyComponentProperties } from '../../utils'

type Props = {
style?: UnistylesValues
imageStyle?: UnistylesValues
}

export const ImageBackground = forwardRef<unknown, Props>((props, forwardedRef) => {
const UnistylesImageBackground = forwardRef<unknown, Props>((props, forwardedRef) => {
let storedRef: NativeImageBackground | null = null
let storedImageRef: NativeImageBackground | null = null
const styleClassNames = getClassName(props.style)
Expand Down Expand Up @@ -54,3 +55,5 @@ export const ImageBackground = forwardRef<unknown, Props>((props, forwardedRef)
/>
)
})

export const ImageBackground = copyComponentProperties(NativeImageBackground, UnistylesImageBackground)
51 changes: 28 additions & 23 deletions src/core/createUnistylesElement.native.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { useLayoutEffect, useRef } from 'react'
import { UnistylesShadowRegistry } from '../specs'
import { passForwardedRef } from './passForwardRef'
import { maybeWarnAboutMultipleUnistyles } from './warn'
import { copyComponentProperties } from '../utils'

const getNativeRef = (Component: any, ref: any) => {
switch (Component.name) {
Expand All @@ -16,31 +17,35 @@ const getNativeRef = (Component: any, ref: any) => {
}
}

export const createUnistylesElement = (Component: any) => React.forwardRef((props, forwardedRef) => {
const storedRef = useRef<unknown>(null)
export const createUnistylesElement = (Component: any) => {
const UnistylesComponent = React.forwardRef((props, forwardedRef) => {
const storedRef = useRef<unknown>(null)

useLayoutEffect(() => {
return () => {
if (storedRef.current) {
// @ts-ignore
UnistylesShadowRegistry.remove(storedRef.current)
useLayoutEffect(() => {
return () => {
if (storedRef.current) {
// @ts-ignore
UnistylesShadowRegistry.remove(storedRef.current)
}
}
}
}, [])
}, [])

return (
<Component
{...props}
ref={(ref: unknown) => {
if (ref) {
storedRef.current = getNativeRef(Component, ref)
}
return (
<Component
{...props}
ref={(ref: unknown) => {
if (ref) {
storedRef.current = getNativeRef(Component, ref)
}

passForwardedRef(props, ref, forwardedRef)
passForwardedRef(props, ref, forwardedRef)

// @ts-ignore we don't know the type of the component
maybeWarnAboutMultipleUnistyles(props.style, Component.displayName)
}}
/>
)
})
// @ts-ignore we don't know the type of the component
maybeWarnAboutMultipleUnistyles(props.style, Component.displayName)
}}
/>
)
})

return copyComponentProperties(Component, UnistylesComponent)
}
63 changes: 34 additions & 29 deletions src/core/createUnistylesElement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,45 @@ import { getClassName } from './getClassname'
import { isServer } from '../web/utils'
import { UnistylesShadowRegistry } from '../web'
import { maybeWarnAboutMultipleUnistyles } from './warn'
import { copyComponentProperties } from '../utils'

type ComponentProps = {
style?: UnistylesValues | Array<UnistylesValues>
}

export const createUnistylesElement = (Component: any) => React.forwardRef<unknown, ComponentProps>((props, forwardedRef) => {
let storedRef: HTMLElement | null = null
const classNames = getClassName(props.style)
export const createUnistylesElement = (Component: any) => {
const UnistylesComponent = React.forwardRef<unknown, ComponentProps>((props, forwardedRef) => {
let storedRef: HTMLElement | null = null
const classNames = getClassName(props.style)

return (
<Component
{...props}
style={classNames}
ref={isServer() ? undefined : (ref: HTMLElement | null) => {
// @ts-ignore we don't know the type of the component
maybeWarnAboutMultipleUnistyles(props.style, Component.displayName)
return (
<Component
{...props}
style={classNames}
ref={isServer() ? undefined : (ref: HTMLElement | null) => {
// @ts-ignore we don't know the type of the component
maybeWarnAboutMultipleUnistyles(props.style, Component.displayName)

if (!ref) {
if (!ref) {
// @ts-expect-error hidden from TS
UnistylesShadowRegistry.remove(storedRef, classNames?.hash)
}

storedRef = ref
// @ts-expect-error hidden from TS
UnistylesShadowRegistry.remove(storedRef, classNames?.hash)
}

storedRef = ref
// @ts-expect-error hidden from TS
UnistylesShadowRegistry.add(ref, classNames?.hash)

if (typeof forwardedRef === 'function') {
return forwardedRef(ref)
}

if (forwardedRef) {
forwardedRef.current = ref
}
}}
/>
)
})
UnistylesShadowRegistry.add(ref, classNames?.hash)

if (typeof forwardedRef === 'function') {
return forwardedRef(ref)
}

if (forwardedRef) {
forwardedRef.current = ref
}
}}
/>
)
})

return copyComponentProperties(Component, UnistylesComponent)
}
71 changes: 38 additions & 33 deletions src/core/createUnistylesImageBackground.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,43 +3,48 @@ import type { Image, ImageBackground, ImageBackgroundProps } from 'react-native'
import { UnistylesShadowRegistry } from '../specs'
import { passForwardedRef } from './passForwardRef'
import { maybeWarnAboutMultipleUnistyles } from './warn'
import { copyComponentProperties } from '../utils'

export const createUnistylesImageBackground = (Component: typeof ImageBackground) => React.forwardRef<ImageBackground, ImageBackgroundProps>((props, forwardedRef) => {
const storedImageRef = useRef<Image | null>(null)
export const createUnistylesImageBackground = (Component: typeof ImageBackground) => {
const UnistylesImageBackground = React.forwardRef<ImageBackground, ImageBackgroundProps>((props, forwardedRef) => {
const storedImageRef = useRef<Image | null>(null)

useLayoutEffect(() => {
return () => {
if (storedImageRef.current) {
// @ts-ignore
UnistylesShadowRegistry.remove(storedImageRef.current)
useLayoutEffect(() => {
return () => {
if (storedImageRef.current) {
// @ts-ignore
UnistylesShadowRegistry.remove(storedImageRef.current)
}
}
}
}, [])
}, [])

// @ts-expect-error we don't know the type of the component
maybeWarnAboutMultipleUnistyles(props.style, 'ImageBackground')
// @ts-ignore we don't know the type of the component
maybeWarnAboutMultipleUnistyles(props.imageStyle, 'ImageBackground')
// @ts-expect-error we don't know the type of the component
maybeWarnAboutMultipleUnistyles(props.style, 'ImageBackground')
// @ts-ignore we don't know the type of the component
maybeWarnAboutMultipleUnistyles(props.imageStyle, 'ImageBackground')

return (
<Component
{...props}
ref={ref => {
passForwardedRef(props, ref, forwardedRef)
return (
<Component
{...props}
ref={ref => {
passForwardedRef(props, ref, forwardedRef)

return () => {
// @ts-ignore
UnistylesShadowRegistry.remove(ref)
}
}}
imageRef={ref => {
if (ref) {
storedImageRef.current = ref
}
return () => {
// @ts-ignore
UnistylesShadowRegistry.remove(ref)
}
}}
imageRef={ref => {
if (ref) {
storedImageRef.current = ref
}

// @ts-expect-error web types are not compatible with RN styles
UnistylesShadowRegistry.add(ref, props.imageStyle)
}}
/>
)
})

// @ts-expect-error web types are not compatible with RN styles
UnistylesShadowRegistry.add(ref, props.imageStyle)
}}
/>
)
})
return copyComponentProperties(Component, UnistylesImageBackground)
}
14 changes: 14 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,17 @@ export const deepMergeObjects = <T extends Record<PropertyKey, any>>(...sources:

return target
}

export const copyComponentProperties = (Component: any, UnistylesComponent: any) => {
Object.entries(Object.getOwnPropertyDescriptors(Component)).forEach(([key, propertyDescriptor]) => {
// Filter out the keys we don't want to copy
if (['$$typeof', 'render'].includes(key)) {
return
}

// @ts-expect-error Copy extra component properties - example: Image.getSize, Image.displayName
UnistylesComponent[key] = propertyDescriptor.value ?? propertyDescriptor.get()
})

return UnistylesComponent
}

0 comments on commit d2a232f

Please sign in to comment.