Last active
August 1, 2023 13:16
-
-
Save mrousavy/bc3b748c56330bec09f128ecb4be0acb to your computer and use it in GitHub Desktop.
`useStyle` - a typed `useMemo` for styles which also includes style flattening and searching
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { DependencyList, useMemo } from "react"; | |
import { | |
ImageStyle, | |
RegisteredStyle, | |
StyleProp, | |
StyleSheet, | |
TextStyle, | |
ViewStyle, | |
} from "react-native"; | |
/** | |
* A hook to memoize a style. Uses `ViewStyle` per default, but can be used with other styles deriving from `FlexStyle` as well, such as `TextStyle`. | |
* @param styleFactory The function that returns a style | |
* @param deps The dependencies to trigger memoization re-evaluation | |
* @example | |
* | |
* const style = useStyle(() => ({ height: someDynamicValue }), [someDynamicValue]) | |
*/ | |
export const useStyle = < | |
TStyle extends ViewStyle | TextStyle | ImageStyle, | |
TOutput extends StyleProp<TStyle> | |
>( | |
styleFactory: () => TOutput, | |
deps?: DependencyList | |
): TOutput => | |
// eslint-disable-next-line react-hooks/exhaustive-deps | |
useMemo(styleFactory, deps); | |
/** | |
* A hook to memoize a style and flatten it into a single object. Uses `ViewStyle` per default, but can be used with other styles deriving from `FlexStyle` as well, such as `TextStyle`. | |
* @param styleFactory The function that returns a style | |
* @param deps The dependencies to trigger memoization re-evaluation | |
* @example | |
* | |
* const style = useStyle(() => ({ height: someDynamicValue }), [someDynamicValue]) | |
*/ | |
export const useFlatStyle = < | |
TStyle extends ViewStyle | TextStyle | ImageStyle, | |
TOutput extends StyleProp<TStyle> | |
>( | |
styleFactory: () => TOutput, | |
deps?: DependencyList | |
): TStyle extends (infer U)[] ? U : TStyle => | |
// eslint-disable-next-line react-hooks/exhaustive-deps | |
useMemo(() => StyleSheet.flatten(styleFactory()), deps); | |
const isRegisteredStyle = <T>( | |
style: T | unknown | |
): style is RegisteredStyle<T> => { | |
if (typeof style === "object" && style != null) | |
return "__registeredStyleBrand" in style; | |
else return false; | |
}; | |
/** | |
* Find a specific value in the given style | |
* @param style The style to search the given key in | |
* @param stylePropertyKey The style property to search for | |
* @returns The value of the found style property, or `undefined` if not found | |
*/ | |
export const findStyle = < | |
TStyle extends ViewStyle | TextStyle | ImageStyle, | |
TResult extends TStyle extends (infer U)[] ? U : TStyle, | |
TName extends keyof TResult | |
>( | |
style: StyleProp<TStyle>, | |
stylePropertyKey: TName | |
): TResult[TName] | undefined => { | |
if (Array.isArray(style)) { | |
// we're doing a reverse loop because values in elements at the end override values at the beginning | |
for (let i = style.length - 1; i >= 0; i--) { | |
const result = findStyle<TStyle, TResult, TName>( | |
// @ts-expect-error it's complaining because it is `readonly`, but we're not modifying it anyways. StyleProp<T>::RecursiveArray<T> needs to be readonly. | |
style[i], | |
stylePropertyKey | |
); | |
if (result != null) return result; | |
} | |
// style not found in array | |
return undefined; | |
} else { | |
if (style == null) { | |
// null, undefined | |
return undefined; | |
} else if (typeof style === "boolean") { | |
// false | |
return undefined; | |
} else if (isRegisteredStyle(style)) { | |
// RegisteredStyle<T> (number) - does not actually exist. | |
// @ts-expect-error typings for StyleProp<> are really hard | |
// eslint-disable-next-line @typescript-eslint/no-unsafe-return | |
return style.__registeredStyleBrand[stylePropertyKey]; | |
} else if (typeof style === "object") { | |
// { ... } | |
// @ts-expect-error typings for StyleProp<> are really hard | |
// eslint-disable-next-line @typescript-eslint/no-unsafe-return | |
return style[stylePropertyKey]; | |
} else { | |
// it's not a known style type. | |
return undefined; | |
} | |
} | |
}; |
yes that's what I meant. I needed this for a custom view that did some special processing when the user passes a style with borderRadius
, that's why I originally created it. I also thought that it might be faster (performance wise), but that has to be benchmarked. (not sure if Yoga natively supports arrays for style)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@mrousavy I'm not sure I understand, could u please give an example,
Edit:
aha I get it now
that's what you meant right ^^ ( sorry it took me sometimes to understand 😑 ).
I'm not sure when would I need to access a property on the style object thou.