From e90fd0cb44e8a4eeea940486a822c76d864274cb Mon Sep 17 00:00:00 2001 From: Ethan Sharabi Date: Tue, 26 Jul 2022 16:59:32 +0300 Subject: [PATCH 1/5] Add new infra for modifiers and themeProps hooks --- src/commons/modifiers.ts | 9 +++++---- src/hooks/index.ts | 2 ++ src/hooks/useModifiers/index.ts | 11 +++++++++++ src/hooks/useThemeProps/index.ts | 13 +++++++++++++ src/style/themeManager.ts | 11 ++++++++++- 5 files changed, 41 insertions(+), 5 deletions(-) create mode 100644 src/hooks/useModifiers/index.ts create mode 100644 src/hooks/useThemeProps/index.ts diff --git a/src/commons/modifiers.ts b/src/commons/modifiers.ts index a7829df073..7d5e3e641b 100644 --- a/src/commons/modifiers.ts +++ b/src/commons/modifiers.ts @@ -43,7 +43,7 @@ export type ModifiersOptions = { alignments?: boolean; flex?: boolean; position?: boolean; -} +}; const PADDING_VARIATIONS = { padding: 'padding', @@ -333,9 +333,10 @@ export function extractComponentProps(component: any, props: Dictionary, ig } //@ts-ignore -export function getThemeProps(props = this.props, context = this.context) { - //@ts-ignore - const componentName = this.displayName || this.constructor.displayName || this.constructor.name; +export function getThemeProps(props = this.props, context = this.context, componentDisplayName = '') { + const componentName = + //@ts-ignore + componentDisplayName || this.displayName || this.constructor.displayName || this.constructor.name; let themeProps; if (_.isFunction(ThemeManager.components[componentName])) { diff --git a/src/hooks/index.ts b/src/hooks/index.ts index c7b78e6d84..3de150c2eb 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -2,10 +2,12 @@ export {default as useCombinedRefs} from './useCombinedRefs'; export {default as useToggleValue} from './useToggleValue'; export {default as useDidUpdate} from './useDidUpdate'; export {default as useMeasure} from './useMeasure'; +export {default as useModifiers} from './useModifiers'; export {default as useOrientation} from './useOrientation'; export {default as useScrollEnabler} from './useScrollEnabler'; export {default as useScrollReached} from './useScrollReached'; export {default as useScrollToItem} from './useScrollToItem'; export {default as useScrollTo} from './useScrollTo'; +export {default as useThemeProps} from './useThemeProps'; export * from './useScrollTo'; diff --git a/src/hooks/useModifiers/index.ts b/src/hooks/useModifiers/index.ts new file mode 100644 index 0000000000..ce255059d2 --- /dev/null +++ b/src/hooks/useModifiers/index.ts @@ -0,0 +1,11 @@ +import {useMemo} from 'react'; +import * as Modifiers from '../../commons/modifiers'; + +const useModifiers = (props: any, options: Modifiers.ModifiersOptions) => { + const modifiers = useMemo(() => { + return Modifiers.generateModifiersStyle(options, props); + }, [props]); + return modifiers; +}; + +export default useModifiers; diff --git a/src/hooks/useThemeProps/index.ts b/src/hooks/useThemeProps/index.ts new file mode 100644 index 0000000000..f62e7ff321 --- /dev/null +++ b/src/hooks/useThemeProps/index.ts @@ -0,0 +1,13 @@ +import {useContext, createContext} from 'react'; +import * as Modifiers from '../../commons/modifiers'; +import {ThemeManager} from 'style'; + +const EmptyContext = createContext({}); + +const useThemeProps = (props: any, componentName: string) => { + const themeContext = ThemeManager.getThemeContext(); + const context = useContext(themeContext ?? EmptyContext); + return Modifiers.getThemeProps(props, context, componentName); +}; + +export default useThemeProps; diff --git a/src/style/themeManager.ts b/src/style/themeManager.ts index 04a631da92..af20dbd510 100644 --- a/src/style/themeManager.ts +++ b/src/style/themeManager.ts @@ -1,8 +1,9 @@ import _ from 'lodash'; +import type {Context} from 'react'; import Colors from './colors'; - export class ThemeManager { + private themeContext?: Context; theme = { primaryColor: Colors.primary, @@ -19,6 +20,14 @@ export class ThemeManager { forcedTheme = { components: {} as Extendable + }; + + setThemeContext(context: Context) { + this.themeContext = context; + } + + getThemeContext() { + return this.themeContext; } getTheme() { From a5baa77b9870c11914ca63ed3cd4dbae871d6646 Mon Sep 17 00:00:00 2001 From: Ethan Sharabi Date: Tue, 26 Jul 2022 17:33:15 +0300 Subject: [PATCH 2/5] Convert view to function component and use modifiers and theme props hooks --- src/components/chipsInput/index.tsx | 1 + src/components/toast/index.js | 12 +- src/components/view/index.tsx | 164 ++++++++++++---------------- 3 files changed, 78 insertions(+), 99 deletions(-) diff --git a/src/components/chipsInput/index.tsx b/src/components/chipsInput/index.tsx index ffaa60b302..8879039ce8 100644 --- a/src/components/chipsInput/index.tsx +++ b/src/components/chipsInput/index.tsx @@ -484,6 +484,7 @@ class ChipsInput extends Component { const Container = maxHeight ? ScrollView : View; return ( {!isTop && !!toastHeight && this.renderAttachmentContent()} - {this.renderContent()} - + {isTop && !!toastHeight && this.renderAttachmentContent()} - + ); } } diff --git a/src/components/view/index.tsx b/src/components/view/index.tsx index bf5ab39eb6..4fa970225b 100644 --- a/src/components/view/index.tsx +++ b/src/components/view/index.tsx @@ -1,14 +1,8 @@ -import React, {PureComponent} from 'react'; +import {useModifiers, useThemeProps} from 'hooks'; +import React, {useEffect, useMemo, useState} from 'react'; import {View as RNView, SafeAreaView, Animated, ViewProps as RNViewProps, StyleProp, ViewStyle} from 'react-native'; import Reanimated from 'react-native-reanimated'; -import { - Constants, - asBaseComponent, - forwardRef, - BaseComponentInjectedProps, - ForwardRefInjectedProps, - ContainerModifiers -} from '../../commons/new'; +import {Constants, ContainerModifiers} from '../../commons/new'; export interface ViewProps extends Omit, ContainerModifiers { /** @@ -46,11 +40,15 @@ export interface ViewProps extends Omit, ContainerModifier style?: StyleProp>; } -type PropsTypes = BaseComponentInjectedProps & ForwardRefInjectedProps & ViewProps; - -interface ViewState { - ready: boolean; -} +const modifiersOptions = { + backgroundColor: true, + borderRadius: true, + paddings: true, + margins: true, + alignments: true, + flex: true, + position: true +}; /** * @description: An enhanced View component @@ -58,94 +56,74 @@ interface ViewState { * @extendsLink: https://reactnative.dev/docs/view * @modifiers: margins, paddings, alignments, background, borderRadius */ -class View extends PureComponent { - static displayName = 'View'; - private Container: React.ClassType; - - constructor(props: PropsTypes) { - super(props); - - this.Container = props.useSafeArea && Constants.isIOS ? SafeAreaView : RNView; - if (props.reanimated) { - this.Container = Reanimated.createAnimatedComponent(this.Container); - } else if (props.animated) { - this.Container = Animated.createAnimatedComponent(this.Container); - } - - this.state = { - ready: !props.renderDelay - }; - } +function View(props: ViewProps, ref: any) { + const themeProps = useThemeProps(props, 'View'); + const { + renderDelay, + style, + // (!) extract left, top, bottom... props to avoid passing them on Android + /* eslint-disable */ + left, + top, + right, + bottom, + flex: propsFlex, + /* eslint-enable */ + inaccessible, + useSafeArea, + animated, + reanimated, + children, + ...others + } = themeProps; + const {backgroundColor, borderRadius, paddings, margins, alignments, flexStyle, positionStyle} = useModifiers(themeProps, + modifiersOptions); + const [ready, setReady] = useState(!renderDelay); - componentDidMount() { - const {renderDelay} = this.props; + useEffect(() => { if (renderDelay) { setTimeout(() => { - this.setState({ready: true}); + setReady(true); }, renderDelay); } - } + }, []); - // TODO: do we need this? - setNativeProps(nativeProps: any) { - //@ts-ignore - this._root.setNativeProps(nativeProps); // eslint-disable-line - } + const ViewContainer = useMemo(() => { + const container = useSafeArea && Constants.isIOS ? SafeAreaView : RNView; - render() { - if (!this.state.ready) { - return null; + if (reanimated) { + return Reanimated.createAnimatedComponent(container); + } else if (animated) { + return Animated.createAnimatedComponent(container); } - // (!) extract left, top, bottom... props to avoid passing them on Android - // eslint-disable-next-line - const { - modifiers, - style, - /* eslint-disable */ - left, - top, - right, - bottom, - flex: propsFlex, - /* eslint-enable */ - forwardedRef, - inaccessible, - ...others - } = this.props; - const {backgroundColor, borderRadius, paddings, margins, alignments, flexStyle, positionStyle} = modifiers; - const Element = this.Container; - return ( - - {this.props.children} - - ); + return container; + }, [useSafeArea, animated, reanimated]); + + if (!ready) { + return null; } -} -const modifiersOptions = { - backgroundColor: true, - borderRadius: true, - paddings: true, - margins: true, - alignments: true, - flex: true, - position: true -}; + return ( + + {children} + + ); +} -export default asBaseComponent(forwardRef(View), {modifiersOptions}); +export default React.forwardRef(View); From 6bc6566b33ef806a79eb91714cd09c420ed9832b Mon Sep 17 00:00:00 2001 From: Ethan Sharabi Date: Wed, 27 Jul 2022 12:34:01 +0300 Subject: [PATCH 3/5] Create global ThemeComponent interface --- src/components/view/index.tsx | 2 +- typings/globalTypes.d.ts | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/view/index.tsx b/src/components/view/index.tsx index 4fa970225b..dcd1ae2e2f 100644 --- a/src/components/view/index.tsx +++ b/src/components/view/index.tsx @@ -4,7 +4,7 @@ import {View as RNView, SafeAreaView, Animated, ViewProps as RNViewProps, StyleP import Reanimated from 'react-native-reanimated'; import {Constants, ContainerModifiers} from '../../commons/new'; -export interface ViewProps extends Omit, ContainerModifiers { +export interface ViewProps extends Omit, ThemeComponent, ContainerModifiers { /** * If true, will render as SafeAreaView */ diff --git a/typings/globalTypes.d.ts b/typings/globalTypes.d.ts index 995873f030..e02375ce40 100644 --- a/typings/globalTypes.d.ts +++ b/typings/globalTypes.d.ts @@ -14,3 +14,7 @@ declare module 'react-native-measureme'; interface Extendable { [key: string]: any; } + +interface ThemeComponent { + useCustomTheme?: boolean; +} From a7c28c71b0910e07d34300dda8d3eacb3465f969 Mon Sep 17 00:00:00 2001 From: Ethan Sharabi Date: Wed, 27 Jul 2022 15:38:06 +0300 Subject: [PATCH 4/5] Remove redundant ThemeComponent type in asBaseComponent --- src/commons/asBaseComponent.tsx | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/commons/asBaseComponent.tsx b/src/commons/asBaseComponent.tsx index 3a476b018f..1292840701 100644 --- a/src/commons/asBaseComponent.tsx +++ b/src/commons/asBaseComponent.tsx @@ -18,11 +18,6 @@ export interface AsBaseComponentOptions { modifiersOptions?: Modifiers.ModifiersOptions; } -// TODO: find a proper way to inject this type in the private repo -type ThemeComponent = { - useCustomTheme?: boolean; -}; - const EMPTY_MODIFIERS = {}; function asBaseComponent(WrappedComponent: React.ComponentType, From 0498c808218f3b45c9352e786923103444054ec0 Mon Sep 17 00:00:00 2001 From: Ethan Sharabi Date: Wed, 27 Jul 2022 16:31:29 +0300 Subject: [PATCH 5/5] memoize view style --- src/components/view/index.tsx | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/components/view/index.tsx b/src/components/view/index.tsx index dcd1ae2e2f..ab249dfb1d 100644 --- a/src/components/view/index.tsx +++ b/src/components/view/index.tsx @@ -100,6 +100,23 @@ function View(props: ViewProps, ref: any) { return container; }, [useSafeArea, animated, reanimated]); + const _style = useMemo(() => { + return [ + backgroundColor && { + backgroundColor + }, + borderRadius && { + borderRadius + }, + flexStyle, + positionStyle, + paddings, + margins, + alignments, + style + ]; + }, [backgroundColor, borderRadius, flexStyle, positionStyle, paddings, margins, alignments, style]); + if (!ready) { return null; } @@ -109,16 +126,7 @@ function View(props: ViewProps, ref: any) { accessibilityElementsHidden={inaccessible} importantForAccessibility={inaccessible ? 'no-hide-descendants' : undefined} {...others} - style={[ - backgroundColor && {backgroundColor}, - borderRadius && {borderRadius}, - flexStyle, - positionStyle, - paddings, - margins, - alignments, - style - ]} + style={_style} ref={ref} > {children}