Skip to content

Commit

Permalink
hash append for StateSelector edge cases
Browse files Browse the repository at this point in the history
  • Loading branch information
ygshbht committed Mar 8, 2024
1 parent 18717ea commit 3ba97a5
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 4 deletions.
12 changes: 11 additions & 1 deletion src/components/StateSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import {useRoute} from '@react-navigation/native';
import type {ParamListBase, RouteProp} from '@react-navigation/native';
import {CONST as COMMON_CONST} from 'expensify-common/lib/CONST';
import React, {useEffect, useState} from 'react';
import type {ForwardedRef} from 'react';
Expand All @@ -12,6 +14,8 @@ import FormHelpMessage from './FormHelpMessage';
import type {MenuItemProps} from './MenuItem';
import MenuItemWithTopDescription from './MenuItemWithTopDescription';

type CustomParamList = ParamListBase & Record<string, Record<string, string>>;

type State = keyof typeof COMMON_CONST.STATES;

type StateSelectorProps = {
Expand Down Expand Up @@ -40,6 +44,12 @@ function StateSelector({errorText, shouldUseStateFromUrl = true, value: stateCod
const stateFromUrl = useGeographicalStateFromRoute();
const [stateToDisplay, setStateToDisplay] = useState<State | ''>('');

/**
* See {@link module:src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx#withHash} for more information.
*/
const route = useRoute<RouteProp<CustomParamList, string>>();
const rawStateFromUrl = route.params?.state as string | undefined;

useEffect(() => {
if (!shouldUseStateFromUrl || !stateFromUrl) {
return;
Expand All @@ -52,7 +62,7 @@ function StateSelector({errorText, shouldUseStateFromUrl = true, value: stateCod
setStateToDisplay(stateFromUrl);

// eslint-disable-next-line react-hooks/exhaustive-deps
}, [stateFromUrl, shouldUseStateFromUrl]);
}, [rawStateFromUrl, shouldUseStateFromUrl]);

useEffect(() => {
if (!stateCode) {
Expand Down
7 changes: 6 additions & 1 deletion src/hooks/useGeographicalStateFromRoute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ import {CONST as COMMON_CONST} from 'expensify-common/lib/CONST';
type CustomParamList = ParamListBase & Record<string, Record<string, string>>;
type State = keyof typeof COMMON_CONST.STATES;

/**
* See {@link module:src/pages/settings/Profile/PersonalDetails/StateSelectionPage.tsx#withHash} for more information.
*/
const removeHash = (arg: string): string => arg.replace(/-hash-.*$/, '');

/**
* Extracts the 'state' (default) query parameter from the route/ url and validates it against COMMON_CONST.STATES, returning its ISO code or `undefined`.
* Example: const stateISO = useGeographicalStateFromRoute(); // Assuming 'state' param is 'CA' or another valid state, returns the corresponding ISO code or `undefined` if invalid.
Expand All @@ -16,5 +21,5 @@ export default function useGeographicalStateFromRoute(stateParamName = 'state'):
if (!stateFromUrlTemp) {
return;
}
return COMMON_CONST.STATES[stateFromUrlTemp as State].stateISO;
return COMMON_CONST.STATES[removeHash(stateFromUrlTemp) as State].stateISO;
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ function StateSelectionPage() {
(option: CountryData) => {
const backTo = params?.backTo ?? '';

// Add hash to param for rare cases, such as in ReimbursementAccountPage (currentStep = CONST.BANK_ACCOUNT.STEP.COMPANY), when the URL on the form page (which has the StateSelector component) already includes a state param. If the form then updates the StateInput with state data from another source and the user attempts to use StateSelector to select the same state present in the URL, it will cause the StateSelector not to detect the state change, as the URL remains the same.
const withHash = (arg: string): string => `${arg}-hash-${Math.random().toString(36).substring(2, 8)}`;

// Determine navigation action based on "backTo" presence and route stack length.
if (navigation.getState()?.routes.length === 1) {
// If this is the only page in the navigation stack (examples include direct navigation to this page via URL or page reload).
Expand All @@ -64,11 +67,11 @@ function StateSelectionPage() {
Navigation.goBack();
} else {
// "backTo" provided: navigate back to "backTo" with state parameter.
Navigation.goBack(appendParam(backTo, 'state', option.value) as Route);
Navigation.goBack(appendParam(backTo, 'state', withHash(option.value)) as Route);
}
} else if (!_.isEmpty(backTo)) {
// Most common case: Navigation stack has multiple routes and "backTo" is defined: navigate to "backTo" with state parameter.
Navigation.navigate(appendParam(backTo, 'state', option.value) as Route);
Navigation.navigate(appendParam(backTo, 'state', withHash(option.value)) as Route);
} else {
// This is a fallback block and should never execute if StateSelector is correctly appending the "backTo" route.
// Navigation stack has multiple routes but no "backTo" defined: default back navigation.
Expand Down

0 comments on commit 3ba97a5

Please sign in to comment.