react-native-unistyles
Version:
Level up your React Native StyleSheet
115 lines (90 loc) • 3.85 kB
text/typescript
import { NitroModules } from 'react-native-nitro-modules'
import type { UnistylesShadowRegistry as UnistylesShadowRegistrySpec } from './ShadowRegistry.nitro'
import type { ShadowNode, Unistyle, ViewHandle } from './types'
interface ShadowRegistry extends UnistylesShadowRegistrySpec {
// Babel API
add(handle?: ViewHandle, styles?: Array<Unistyle>): void
remove(handle?: ViewHandle): void
// JSI
link(node: ShadowNode, styles?: Array<Unistyle>): void
unlink(node: ShadowNode): void
suspend(node: ShadowNode): void
flush(): void
setScopedTheme(themeName?: string): void
getScopedTheme(): string | undefined
}
const HybridShadowRegistry = NitroModules.createHybridObject<ShadowRegistry>('UnistylesShadowRegistry')
const SUSPENSE_TAG = 13
const isInsideSuspendedBoundary = (fiber: any): boolean => {
let current = fiber?.return
while (current) {
if (current.tag === SUSPENSE_TAG && current.memoizedState !== null) {
return true
}
current = current.return
}
return false
}
const findFiberForHandle = (handle: ViewHandle) => {
return (
handle?.__internalInstanceHandle ??
handle?.getScrollResponder?.()?.getNativeScrollRef?.()?.__internalInstanceHandle ??
handle?.getNativeScrollRef?.()?.__internalInstanceHandle ??
handle?._viewRef?.__internalInstanceHandle ??
handle?.viewRef?.current?.__internalInstanceHandle ??
handle?._nativeRef?.__internalInstanceHandle
)
}
const findShadowNodeForHandle = (handle: ViewHandle) => {
const node =
handle?.__internalInstanceHandle?.stateNode?.node ??
handle?.getScrollResponder?.()?.getNativeScrollRef?.()?.__internalInstanceHandle?.stateNode?.node ??
handle?.getNativeScrollRef?.()?.__internalInstanceHandle?.stateNode?.node ??
handle?._viewRef?.__internalInstanceHandle?.stateNode?.node ??
handle?.viewRef?.current?.__internalInstanceHandle?.stateNode?.node ??
handle?._nativeRef?.__internalInstanceHandle?.stateNode?.node
// @ts-ignore we don't know the type of handle
if (!node && handle?.props?.horizontal && handle?.constructor?.name === 'FlatList') {
throw new Error(
'Unistyles: detected an unsupported FlatList with the horizontal prop. This will cause crashes on Android due to a bug in React Native core. Read more: https://github.com/facebook/react-native/issues/51601',
)
}
return node
}
HybridShadowRegistry.add = (handle, styles) => {
// virtualized nodes can be null
if (!handle || !styles) {
return
}
const stylesArray = Array.isArray(styles) ? styles.flat() : [styles]
// filter styles that are undefined or with no keys
const filteredStyles = stylesArray
.filter((style) => style && Object.keys(style).length > 0)
.flat()
.filter(Boolean)
if (filteredStyles.length > 0) {
const node = findShadowNodeForHandle(handle)
if (!node) {
throw new Error(
`Unistyles: Could not find shadow node for one of your components of type ${handle?.constructor?.name ?? 'unknown'}`,
)
}
HybridShadowRegistry.link(node, filteredStyles)
}
}
HybridShadowRegistry.remove = (handle) => {
if (!handle) {
return
}
const maybeNode = findShadowNodeForHandle(handle)
if (maybeNode) {
const fiber = findFiberForHandle(handle)
if (fiber && isInsideSuspendedBoundary(fiber)) {
HybridShadowRegistry.suspend(maybeNode)
} else {
HybridShadowRegistry.unlink(maybeNode)
}
}
}
type PrivateMethods = 'add' | 'remove' | 'link' | 'unlink' | 'suspend'
export const UnistylesShadowRegistry = HybridShadowRegistry as Omit<ShadowRegistry, PrivateMethods>