Skip to content

Instantly share code, notes, and snippets.

@mrousavy
Last active August 1, 2023 13:16
Show Gist options
  • Save mrousavy/bc3b748c56330bec09f128ecb4be0acb to your computer and use it in GitHub Desktop.
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
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;
}
}
};
@mrousavy
Copy link
Author

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