-
-
Save mrousavy/bc3b748c56330bec09f128ecb4be0acb to your computer and use it in GitHub Desktop.
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; | |
} | |
} | |
}; |
what's the difference btw
useStyle
&useFlatStyle
@a-eid useStyle
is the same as a useMemo
, useFlatStyle
uses StyleSheet.flatten
, so if you pass an array of styles it flattens all those styles into a single style object.
would these 2 examples be valid use cases for useFlatStyle & useStyle
function Cmp({ insets }){
const viewStyle = useFlatStyle(() => [styles.view, { marginBottom: insets.bottom }], [insets.bottom])
return <View style={viewStyle}/>
}
function Cmp({ insets }){
const viewStyle = useStyle(() => ({ marginBottom: insets.bottom }), [insets.bottom])
return <View style={viewStyle}/>
}
Yes, but if you also use an array in the useStyle
example you wouldn't be able to do viewStyle.marginBottom
to find out the bottom margin. With useFlatStyle
you would, since it merges the two styles into one.
@mrousavy I'm not sure I understand, could u please give an example,
Edit:
aha I get it now
const viewStyle = useStyle(() => [styles.view, { marginBottom: insets.bottom }], [insets.bottom])
console.log(viewStyle.marginBottom) // undefined
// you need to use
console.log(viewStyle[1].marginBottom) // correctly logs insets.bottom
const viewStyle = useFlatStyle(() => [styles.view, { marginBottom: insets.bottom }], [insets.bottom])
console.log(viewStyle.marginBottom) // correctly logs insets.bottom
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.
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)
what's the difference btw
useStyle
&useFlatStyle
, is it okay to useArrays
as styles ?