From 5ce3277a6e3050d0d7a93de67cdc8c6e66948914 Mon Sep 17 00:00:00 2001 From: Tsu-ba-me Date: Wed, 27 Jul 2022 21:44:15 -0400 Subject: [PATCH] fix(striker-ui): don't create functions on re-render --- striker-ui/components/GeneralInitForm.tsx | 214 ++++++++++++---------- striker-ui/components/InputWithRef.tsx | 1 + striker-ui/components/SuggestButton.tsx | 20 +- 3 files changed, 139 insertions(+), 96 deletions(-) diff --git a/striker-ui/components/GeneralInitForm.tsx b/striker-ui/components/GeneralInitForm.tsx index e6fd4c5a..a78d14c2 100644 --- a/striker-ui/components/GeneralInitForm.tsx +++ b/striker-ui/components/GeneralInitForm.tsx @@ -1,11 +1,12 @@ import { Box as MUIBox } from '@mui/material'; -import { forwardRef, useImperativeHandle, useRef } from 'react'; +import { forwardRef, useImperativeHandle, useRef, useState } from 'react'; -import createFunction from '../lib/createFunction'; import FlexBox from './FlexBox'; import InputWithRef, { InputForwardedRefContent } from './InputWithRef'; import isEmpty from '../lib/isEmpty'; -import OutlinedInputWithLabel from './OutlinedInputWithLabel'; +import OutlinedInputWithLabel, { + OutlinedInputWithLabelProps, +} from './OutlinedInputWithLabel'; import pad from '../lib/pad'; import SuggestButton from './SuggestButton'; @@ -19,6 +20,11 @@ type GeneralInitFormForwardRefContent = { }; }; +type OutlinedInputWithLabelOnBlur = Exclude< + OutlinedInputWithLabelProps['inputProps'], + undefined +>['onBlur']; + const MAX_ORGANIZATION_PREFIX_LENGTH = 5; const MIN_ORGANIZATION_PREFIX_LENGTH = 2; const MAX_HOST_NUMBER_LENGTH = 2; @@ -60,6 +66,13 @@ const buildHostName = ({ const GeneralInitForm = forwardRef( (generalInitFormProps, ref) => { + const [ + isShowOrganizationPrefixSuggest, + setIsShowOrganizationPrefixSuggest, + ] = useState(false); + const [isShowHostNameSuggest, setIsShowHostNameSuggest] = + useState(false); + const organizationNameInputRef = useRef>( {}, ); @@ -99,51 +112,48 @@ const GeneralInitForm = forwardRef( return hostName; }; - const populateOrganizationPrefixInputOnBlur = createFunction( - { - condition: - !organizationPrefixInputRef.current.getIsChangedByUser?.call(null), - }, - populateOrganizationPrefixInput, - ); - const populateHostNameInputOnBlur = createFunction( - { condition: !hostNameInputRef.current.getIsChangedByUser?.call(null) }, - populateHostNameInput, - ); - const handleOrganizationPrefixSuggest = createFunction( - { - conditionFn: () => - organizationPrefixInputRef.current.getIsChangedByUser?.call(null) === - true && - isEmpty([organizationNameInputRef.current.getValue?.call(null)], { - not: true, - }), - }, + const isOrganizationPrefixPrereqFilled = () => + isEmpty([organizationNameInputRef.current.getValue?.call(null)], { + not: true, + }); + const isHostNamePrereqFilled = () => + isEmpty( + [ + organizationPrefixInputRef.current.getValue?.call(null), + hostNumberInputRef.current.getValue?.call(null), + domainNameInputRef.current.getValue?.call(null), + ], + { + not: true, + }, + ); + const populateOrganizationPrefixInputOnBlur: OutlinedInputWithLabelOnBlur = () => { - const organizationPrefix = populateOrganizationPrefixInput(); - - if (!hostNameInputRef.current.getIsChangedByUser?.call(null)) { - populateHostNameInput({ organizationPrefix }); + if (organizationPrefixInputRef.current.getIsChangedByUser?.call(null)) { + setIsShowOrganizationPrefixSuggest( + isOrganizationPrefixPrereqFilled(), + ); + } else { + populateOrganizationPrefixInput(); } - }, - ); - const handlerHostNameSuggest = createFunction( - { - conditionFn: () => - hostNameInputRef.current.getIsChangedByUser?.call(null) === true && - isEmpty( - [ - organizationPrefixInputRef.current.getValue?.call(null), - hostNumberInputRef.current.getValue?.call(null), - domainNameInputRef.current.getValue?.call(null), - ], - { - not: true, - }, - ), - }, - populateHostNameInput, - ); + }; + const populateHostNameInputOnBlur: OutlinedInputWithLabelOnBlur = () => { + if (hostNameInputRef.current.getIsChangedByUser?.call(null)) { + setIsShowHostNameSuggest(isHostNamePrereqFilled()); + } else { + populateHostNameInput(); + } + }; + const handleOrganizationPrefixSuggest = () => { + const organizationPrefix = populateOrganizationPrefixInput(); + + if (!hostNameInputRef.current.getIsChangedByUser?.call(null)) { + populateHostNameInput({ organizationPrefix }); + } + }; + const handlerHostNameSuggest = () => { + populateHostNameInput(); + }; useImperativeHandle(ref, () => ({ get: () => ({ @@ -188,32 +198,62 @@ const GeneralInitForm = forwardRef( } ref={organizationNameInputRef} /> - - ), - inputProps: { - maxLength: MAX_ORGANIZATION_PREFIX_LENGTH, - style: { width: '2.5em' }, - }, - onBlur: populateHostNameInputOnBlur, - sx: { - minWidth: 'min-content', - width: 'fit-content', - }, - }} - label="Prefix" - /> - } - ref={organizationPrefixInputRef} - /> + + + ), + inputProps: { + maxLength: MAX_ORGANIZATION_PREFIX_LENGTH, + style: { width: '2.5em' }, + }, + onBlur: populateHostNameInputOnBlur, + sx: { + minWidth: 'min-content', + width: 'fit-content', + }, + }} + label="Prefix" + onChange={() => { + setIsShowOrganizationPrefixSuggest( + isOrganizationPrefixPrereqFilled(), + ); + }} + /> + } + ref={organizationPrefixInputRef} + /> + + } + ref={hostNumberInputRef} + valueType="number" + /> + ( onBlur: populateHostNameInputOnBlur, sx: { minWidth: { sm: '16em' }, - width: { xs: '100%', sm: '50%' }, }, }} label="Domain name" @@ -235,26 +274,7 @@ const GeneralInitForm = forwardRef( } ref={domainNameInputRef} /> - - } - ref={hostNumberInputRef} - valueType="number" - /> + ( id="striker-init-general-host-name" inputProps={{ endAdornment: ( - + ), inputProps: { style: { @@ -276,6 +299,9 @@ const GeneralInitForm = forwardRef( }, }} label="Host name" + onChange={() => { + setIsShowHostNameSuggest(isHostNamePrereqFilled()); + }} /> } ref={hostNameInputRef} diff --git a/striker-ui/components/InputWithRef.tsx b/striker-ui/components/InputWithRef.tsx index 6f114afb..df406497 100644 --- a/striker-ui/components/InputWithRef.tsx +++ b/striker-ui/components/InputWithRef.tsx @@ -66,6 +66,7 @@ const InputWithRef = forwardRef( const onChange = createInputOnChangeHandler({ postSet: (...args) => { setIsChangedByUser(true); + input.props.onChange?.call(null, ...args); postSetAppend?.call(null, ...args); }, set: setValue, diff --git a/striker-ui/components/SuggestButton.tsx b/striker-ui/components/SuggestButton.tsx index e1fc1f63..836a0dba 100644 --- a/striker-ui/components/SuggestButton.tsx +++ b/striker-ui/components/SuggestButton.tsx @@ -2,8 +2,22 @@ import { FC } from 'react'; import ContainedButton, { ContainedButtonProps } from './ContainedButton'; -const SuggestButton: FC = ({ onClick, ...restProps }) => - onClick ? ( +type SuggestButtonOptionalProps = { + show?: boolean; +}; + +type SuggestButtonProps = ContainedButtonProps & SuggestButtonOptionalProps; + +const SUGGEST_BUTTON_DEFAULT_PROPS: Required = { + show: true, +}; + +const SuggestButton: FC = ({ + onClick, + show: isShow = SUGGEST_BUTTON_DEFAULT_PROPS.show, + ...restProps +}) => + isShow ? ( Suggest @@ -11,4 +25,6 @@ const SuggestButton: FC = ({ onClick, ...restProps }) => <> ); +SuggestButton.defaultProps = SUGGEST_BUTTON_DEFAULT_PROPS; + export default SuggestButton;