From cd8474465b38d59cf5e4198c29b966330d425cf6 Mon Sep 17 00:00:00 2001 From: mubbsharanwar Date: Tue, 26 Mar 2024 15:09:54 +0500 Subject: [PATCH] perf: use useSelector with granular approach in registration component to minimize rendrening --- .../EmailField/EmailField.jsx | 6 +- .../UsernameField/UsernameField.jsx | 3 +- src/register/RegistrationPage.jsx | 55 ++++++++----------- src/register/data/selectors.js | 33 +++++++++++ src/register/data/utils.js | 27 --------- 5 files changed, 60 insertions(+), 64 deletions(-) create mode 100644 src/register/data/selectors.js diff --git a/src/register/RegistrationFields/EmailField/EmailField.jsx b/src/register/RegistrationFields/EmailField/EmailField.jsx index 6521f6d7..1f1ddc79 100644 --- a/src/register/RegistrationFields/EmailField/EmailField.jsx +++ b/src/register/RegistrationFields/EmailField/EmailField.jsx @@ -37,10 +37,8 @@ const EmailField = (props) => { confirmEmailValue, } = props; - const { - registrationFormData: backedUpFormData, - validationApiRateLimited, - } = useSelector(state => state.register); + const backedUpFormData = useSelector(state => state.register.registrationFormData); + const validationApiRateLimited = useSelector(state => state.register.validationApiRateLimited); const [emailSuggestion, setEmailSuggestion] = useState({ ...backedUpFormData?.emailSuggestion }); diff --git a/src/register/RegistrationFields/UsernameField/UsernameField.jsx b/src/register/RegistrationFields/UsernameField/UsernameField.jsx index 4d0693d7..59d077c6 100644 --- a/src/register/RegistrationFields/UsernameField/UsernameField.jsx +++ b/src/register/RegistrationFields/UsernameField/UsernameField.jsx @@ -41,7 +41,8 @@ const UsernameField = (props) => { let className = ''; let suggestedUsernameDiv = null; let iconButton = null; - const { usernameSuggestions, validationApiRateLimited } = useSelector(state => state.register); + const usernameSuggestions = useSelector(state => state.register.usernameSuggestions); + const validationApiRateLimited = useSelector(state => state.register.validationApiRateLimited); /** * We need to remove the placeholder from the field, adding a space will do that. diff --git a/src/register/RegistrationPage.jsx b/src/register/RegistrationPage.jsx index 3a9426c2..80ac1f6b 100644 --- a/src/register/RegistrationPage.jsx +++ b/src/register/RegistrationPage.jsx @@ -25,8 +25,9 @@ import { FORM_SUBMISSION_ERROR, TPA_AUTHENTICATION_FAILURE, } from './data/constants'; +import getBackendValidations from './data/selectors'; import { - getBackendValidations, isFormValid, prepareRegistrationPayload, + isFormValid, prepareRegistrationPayload, } from './data/utils'; import messages from './messages'; import { EmailField, NameField, UsernameField } from './RegistrationFields'; @@ -65,38 +66,26 @@ const RegistrationPage = (props) => { institutionLogin, } = props; - const { - registrationFormData: backedUpFormData, - registrationError, - registrationError: { - errorCode: registrationErrorCode, - } = {}, - registrationResult, - shouldBackupState, - userPipelineDataLoaded, - submitState, - validations, - } = useSelector(state => state.register); + const backedUpFormData = useSelector(state => state.register.registrationFormData); + const registrationError = useSelector(state => state.register.registrationError); + const registrationErrorCode = registrationError?.errorCode; + const registrationResult = useSelector(state => state.register.registrationResult); + const shouldBackupState = useSelector(state => state.register.shouldBackupState); + const userPipelineDataLoaded = useSelector(state => state.register.userPipelineDataLoaded); + const submitState = useSelector(state => state.register.submitState); - const { - fieldDescriptions, - optionalFields, - thirdPartyAuthApiStatus, - thirdPartyAuthContext, - thirdPartyAuthContext: { - autoSubmitRegForm, - errorMessage: thirdPartyAuthErrorMessage, - finishAuthUrl, - currentProvider, - providers, - secondaryProviders, - pipelineUserDetails, - }, - } = useSelector(state => state.commonComponents); + const fieldDescriptions = useSelector(state => state.commonComponents.fieldDescriptions); + const optionalFields = useSelector(state => state.commonComponents.optionalFields); + const thirdPartyAuthApiStatus = useSelector(state => state.commonComponents.thirdPartyAuthApiStatus); + const autoSubmitRegForm = useSelector(state => state.commonComponents.thirdPartyAuthContext.autoSubmitRegForm); + const thirdPartyAuthErrorMessage = useSelector(state => state.commonComponents.thirdPartyAuthContext.errorMessage); + const finishAuthUrl = useSelector(state => state.commonComponents.thirdPartyAuthContext.finishAuthUrl); + const currentProvider = useSelector(state => state.commonComponents.thirdPartyAuthContext.currentProvider); + const providers = useSelector(state => state.commonComponents.thirdPartyAuthContext.providers); + const secondaryProviders = useSelector(state => state.commonComponents.thirdPartyAuthContext.secondaryProviders); + const pipelineUserDetails = useSelector(state => state.commonComponents.thirdPartyAuthContext.pipelineUserDetails); - const backendValidations = useMemo( - () => getBackendValidations(registrationError, validations), [registrationError, validations], - ); + const backendValidations = useSelector(getBackendValidations); const queryParams = useMemo(() => getAllPossibleQueryParams(), []); const tpaHint = useMemo(() => getTpaHint(), []); @@ -130,7 +119,9 @@ const RegistrationPage = (props) => { } } }, [ // eslint-disable-line react-hooks/exhaustive-deps - thirdPartyAuthContext, + thirdPartyAuthApiStatus, + thirdPartyAuthErrorMessage, + pipelineUserDetails, userPipelineDataLoaded, ]); diff --git a/src/register/data/selectors.js b/src/register/data/selectors.js new file mode 100644 index 00000000..12811fa1 --- /dev/null +++ b/src/register/data/selectors.js @@ -0,0 +1,33 @@ +import { createSelector } from 'reselect'; + +/** + * Selector for backend validations which processes the api output and generates a + * key value dict for field errors. + * @returns {{username: string}|{name: string}|*|{}|null} + */ +const getRegistrationError = state => state.register.registrationError; +const getValidations = state => state.register.validations; + +const getBackendValidations = createSelector( + [getRegistrationError, getValidations], + (registrationError, validations) => { + if (validations) { + return validations.validationDecisions; + } + + if (Object.keys(registrationError).length > 0) { + const fields = Object.keys(registrationError).filter( + (fieldName) => !(fieldName in ['errorCode', 'usernameSuggestions']), + ); + + const validationDecisions = {}; + fields.forEach(field => { + validationDecisions[field] = registrationError[field][0].userMessage || ''; + }); + return validationDecisions; + } + + return null; + }); + +export default getBackendValidations; diff --git a/src/register/data/utils.js b/src/register/data/utils.js index b044f93d..b0cff129 100644 --- a/src/register/data/utils.js +++ b/src/register/data/utils.js @@ -132,30 +132,3 @@ export const prepareRegistrationPayload = ( payload = { ...payload, ...queryParams }; return payload; }; - -/** - * A helper for backend validations selector. It processes the api output and generates a - * key value dict for field errors. - * @param registrationError - * @param validations - * @returns {{username: string}|{name: string}|*|{}|null} - */ -export const getBackendValidations = (registrationError, validations) => { - if (validations) { - return validations.validationDecisions; - } - - if (Object.keys(registrationError).length > 0) { - const fields = Object.keys(registrationError).filter( - (fieldName) => !(fieldName in ['errorCode', 'usernameSuggestions']), - ); - - const validationDecisions = {}; - fields.forEach(field => { - validationDecisions[field] = registrationError[field][0].userMessage || ''; - }); - return validationDecisions; - } - - return null; -};