From 82286d992134477a99f320eb067497f89cf633f9 Mon Sep 17 00:00:00 2001 From: Aditya Jamuar Date: Thu, 16 Dec 2021 14:16:42 +0530 Subject: [PATCH] feat: checkbox performance improved and added to --- .gitignore | 3 + .../primitives/Checkbox/Checkbox.tsx | 225 ++++++++++-------- .../primitives/Checkbox/Checkbox.web.tsx | 217 ++++++++++------- 3 files changed, 271 insertions(+), 174 deletions(-) diff --git a/.gitignore b/.gitignore index 8b6d2fe44..9cf850aa9 100644 --- a/.gitignore +++ b/.gitignore @@ -56,3 +56,6 @@ android/keystores/debug.keystore lib/ src/jest/mock.ts node_modules + +# personal +.env \ No newline at end of file diff --git a/src/components/primitives/Checkbox/Checkbox.tsx b/src/components/primitives/Checkbox/Checkbox.tsx index d5e401ac2..9dbeaa0a5 100644 --- a/src/components/primitives/Checkbox/Checkbox.tsx +++ b/src/components/primitives/Checkbox/Checkbox.tsx @@ -20,31 +20,33 @@ import { import SizedIcon from './SizedIcon'; const Checkbox = ({ wrapperRef, ...props }: ICheckboxProps, ref: any) => { - const { hoverProps, isHovered } = useHover(); - const { pressableProps, isPressed } = useIsPressed(); - const { focusProps, isFocused } = useFocus(); const formControlContext = useFormControlContext(); - const combinedProps = combineContextAndProps(formControlContext, props); - - const _ref = React.useRef(); - const mergedRef = mergeRefs([ref, _ref]); + const { + isInvalid, + isReadOnly, + isIndeterminate, + ...combinedProps + } = combineContextAndProps(formControlContext, props); + const checkboxGroupContext = useContext(CheckboxGroupContext); const state = useToggleState({ ...combinedProps, defaultSelected: combinedProps.defaultIsChecked, isSelected: combinedProps.isChecked, }); - const groupState = useContext(CheckboxGroupContext); + + const _ref = React.useRef(); + const mergedRef = mergeRefs([ref, _ref]); // Swap hooks depending on whether this checkbox is inside a CheckboxGroup. // This is a bit unorthodox. Typically, hooks cannot be called in a conditional, // but since the checkbox won't move in and out of a group, it should be safe. - const { inputProps } = groupState + const { inputProps: groupItemInputProps } = checkboxGroupContext ? // eslint-disable-next-line react-hooks/rules-of-hooks useCheckboxGroupItem( combinedProps, - groupState.state, + checkboxGroupContext.state, //@ts-ignore mergedRef ) @@ -56,97 +58,134 @@ const Checkbox = ({ wrapperRef, ...props }: ICheckboxProps, ref: any) => { mergedRef ); - const { - checked: isChecked, - disabled: isDisabled, - isInvalid, - isReadOnly, - isIndeterminate, - } = inputProps; + // eslint-disable-next-line react-hooks/exhaustive-deps + const inputProps = React.useMemo(() => groupItemInputProps, [ + groupItemInputProps.checked, + groupItemInputProps.disabled, + ]); - const { - icon, - _interactionBox, - _icon, - // destructuring pressable props and passing it manually - onPress, - onPressIn, - onPressOut, - onHoverIn, - onHoverOut, - onFocus, - onBlur, - ...resolvedProps - } = usePropsResolution('Checkbox', inputProps, { + const [contextCombinedProps] = React.useState({ + ...checkboxGroupContext, + ...combinedProps, + }); + + return ( + + ); +}; +const CheckboxComponent = React.memo( + ({ + wrapperRef, + inputProps, + combinedProps, isInvalid, isReadOnly, isIndeterminate, - isDisabled, - isChecked, - isHovered, - isPressed, - isFocused, - }); + }: any) => { + const _ref = React.useRef(); + const { hoverProps, isHovered } = useHover(); + const { pressableProps, isPressed } = useIsPressed(); + const { focusProps, isFocused } = useFocus(); - const [layoutProps, nonLayoutProps] = extractInObject(resolvedProps, [ - ...stylingProps.margin, - ...stylingProps.layout, - ...stylingProps.flexbox, - ...stylingProps.position, - '_text', - ]); + const { checked: isChecked, disabled: isDisabled } = inputProps; - const [ - accessibilityProps, - nonAccessibilityProps, - ] = extractInObject(nonLayoutProps, [ - 'accessibilityRole', - 'accessibilityState', - ]); + const { + icon, + _interactionBox, + _icon, + // destructuring pressable props and passing it manually + onPress, + onPressIn, + onPressOut, + onHoverIn, + onHoverOut, + onFocus, + onBlur, + ...resolvedProps + } = usePropsResolution('Checkbox', inputProps, { + isInvalid, + isReadOnly, + isIndeterminate, + isDisabled, + isChecked, + isHovered, + isPressed, + isFocused, + }); - //TODO: refactor for responsive prop - if (useHasResponsiveProps(resolvedProps)) { - return null; - } + const [layoutProps, nonLayoutProps] = extractInObject(resolvedProps, [ + ...stylingProps.margin, + ...stylingProps.layout, + ...stylingProps.flexbox, + ...stylingProps.position, + '_text', + ]); - return ( - - -
- {/* Interaction Wrapper */} - - {/* Checkbox */} -
- + const [ + accessibilityProps, + nonAccessibilityProps, + ] = extractInObject(nonLayoutProps, [ + 'accessibilityRole', + 'accessibilityState', + ]); + + //TODO: refactor for responsive prop + if (useHasResponsiveProps(resolvedProps)) { + return null; + } + + return ( + + +
+ {/* Interaction Wrapper */} + + {/* Checkbox */} +
+ +
-
- {/* Label */} - {combinedProps.children} -
- - ); -}; + {/* Label */} + {combinedProps.children} + + + ); + } +); export default memo(forwardRef(Checkbox)); diff --git a/src/components/primitives/Checkbox/Checkbox.web.tsx b/src/components/primitives/Checkbox/Checkbox.web.tsx index 37e7c2f5b..fe7e04a20 100644 --- a/src/components/primitives/Checkbox/Checkbox.web.tsx +++ b/src/components/primitives/Checkbox/Checkbox.web.tsx @@ -1,5 +1,5 @@ -import React, { useContext, memo, forwardRef } from 'react'; -import { mergeRefs } from './../../../utils'; +import React, { memo, forwardRef } from 'react'; +import { mergeRefs } from '../../../utils'; import { usePropsResolution } from '../../../hooks/useThemeProps'; import { Center } from '../../composites/Center'; import { useFormControlContext } from '../../composites/FormControl'; @@ -27,22 +27,19 @@ const Checkbox = ({ wrapperRef, ...props }: ICheckboxProps, ref: any) => { } = combineContextAndProps(formControlContext, props); const checkboxGroupContext = React.useContext(CheckboxGroupContext); - - const _ref = React.useRef(); - const mergedRef = mergeRefs([ref, _ref]); - const state = useToggleState({ ...props, defaultSelected: props.defaultIsChecked, isSelected: props.isChecked, }); - const groupState = useContext(CheckboxGroupContext); - const { isHovered } = useHover({}, _ref); + + const _ref = React.useRef(); + const mergedRef = mergeRefs([ref, _ref]); // Swap hooks depending on whether this checkbox is inside a CheckboxGroup. // This is a bit unorthodox. Typically, hooks cannot be called in a conditional, // but since the checkbox won't move in and out of a group, it should be safe. - const { inputProps } = groupState + const { inputProps: groupItemInputProps } = checkboxGroupContext ? // eslint-disable-next-line react-hooks/rules-of-hooks useCheckboxGroupItem( { @@ -50,7 +47,7 @@ const Checkbox = ({ wrapperRef, ...props }: ICheckboxProps, ref: any) => { 'aria-label': combinedProps.accessibilityLabel, 'value': combinedProps.value, }, - groupState.state, + checkboxGroupContext.state, //@ts-ignore mergedRef ) @@ -65,88 +62,146 @@ const Checkbox = ({ wrapperRef, ...props }: ICheckboxProps, ref: any) => { mergedRef ); - const { checked: isChecked, disabled: isDisabled } = inputProps; + // eslint-disable-next-line react-hooks/exhaustive-deps + const inputProps = React.useMemo(() => groupItemInputProps, [ + groupItemInputProps.checked, + groupItemInputProps.disabled, + ]); + + const [contextCombinedProps] = React.useState({ + ...checkboxGroupContext, + ...combinedProps, + }); + + return ( + + ); +}; + +const CheckboxComponent = React.memo( + ({ + wrapperRef, + inputProps, + combinedProps, + isInvalid, + isReadOnly, + isIndeterminate, + mergedRef, + }: any) => { + const _ref = React.useRef(); + const { isHovered } = useHover({}, _ref); + + const { checked: isChecked, disabled: isDisabled } = inputProps; - const { focusProps, isFocusVisible } = useFocusRing(); + const { focusProps, isFocusVisible } = useFocusRing(); - const { icon, _interactionBox, _icon, ...resolvedProps } = usePropsResolution( - 'Checkbox', - { - ...checkboxGroupContext, - ...combinedProps, - }, - { + const { + icon, + _interactionBox, + _icon, + ...resolvedProps + } = usePropsResolution('Checkbox', combinedProps, { isInvalid, isReadOnly, - isFocusVisible, + isFocusVisible: true, isDisabled, isIndeterminate, isChecked, + isHovered: true, + }); + + const [layoutProps, nonLayoutProps] = extractInObject(resolvedProps, [ + ...stylingProps.margin, + ...stylingProps.layout, + ...stylingProps.flexbox, + ...stylingProps.position, + '_text', + ]); + + const component = React.useMemo(() => { + return ( + +
+ {/* Interaction Box */} + + {/* Checkbox */} +
+ {/* {iconResolver()} */} + +
+
+ {/* Label */} + {resolvedProps?.children} +
+ ); + }, [ + _icon, + _interactionBox, + icon, + isChecked, + isDisabled, + isFocusVisible, isHovered, + isInvalid, + layoutProps, + nonLayoutProps, + resolvedProps?.children, + ]); + + const mergedWrapperRef = React.useMemo( + () => mergeRefs([wrapperRef, _ref]), + [wrapperRef] + ); + + //TODO: refactor for responsive prop + if (useHasResponsiveProps(resolvedProps)) { + return null; } - ); - const [layoutProps, nonLayoutProps] = extractInObject(resolvedProps, [ - ...stylingProps.margin, - ...stylingProps.layout, - ...stylingProps.flexbox, - ...stylingProps.position, - '_text', - ]); + return ( + + + {/* */} + + - const component = ( - -
- {/* Interaction Box */} - - {/* Checkbox */} -
- {/* {iconResolver()} */} - -
-
- {/* Label */} - {resolvedProps?.children} -
- ); - //TODO: refactor for responsive prop - if (useHasResponsiveProps(resolvedProps)) { - return null; + {component} +
+ ); } - return ( - - - - - - {component} - - ); -}; +); export default memo(forwardRef(Checkbox));