Skip to content

Commit cc39327

Browse files
authored
Fix address bar bug (#2773)
1 parent 993c352 commit cc39327

2 files changed

Lines changed: 135 additions & 33 deletions

File tree

client/src/components/FoodSeeker/AddressDropDown.jsx

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,13 @@ export default function AddressDropDown({ autoFocus }) {
2828
searchCoordinates?.locationName || ""
2929
);
3030
const [open, setOpen] = useState(false);
31-
const { mapboxResults, fetchMapboxResults, isLoading } = useMapboxGeocoder();
31+
const {
32+
mapboxResults,
33+
searchMapboxResults,
34+
ensureMapboxResults,
35+
isLoading,
36+
resultsQuery,
37+
} = useMapboxGeocoder();
3238
const dispatch = useAppDispatch();
3339
const navigate = useNavigate();
3440
const { flyTo } = useMapbox();
@@ -76,10 +82,9 @@ export default function AddressDropDown({ autoFocus }) {
7682
const handleInputChange = (delta) => {
7783
if (!delta) return;
7884
const safeValue = typeof delta === "string" ? delta : delta.target.value;
85+
setHighlightedOption(null);
7986
setInputVal(safeValue);
80-
if (safeValue) {
81-
fetchMapboxResults(safeValue);
82-
}
87+
searchMapboxResults(safeValue);
8388
};
8489

8590
const handleAutocompleteOnChange = (selectedResult) => {
@@ -206,12 +211,30 @@ export default function AddressDropDown({ autoFocus }) {
206211
);
207212
};
208213

209-
const handleKeyDown = (event) => {
210-
if (event.key === "Enter" && !isLoading) {
214+
const handleKeyDown = async (event) => {
215+
if (event.key === "Enter") {
211216
event.preventDefault();
212-
if (mapboxOptions.length > 0) {
213-
const selected = highlightedOption ?? mapboxOptions[0];
214-
handleAutocompleteOnChange(selected);
217+
218+
const normalizedInput = inputVal.trim();
219+
if (!normalizedInput) {
220+
return;
221+
}
222+
223+
const hasCurrentAddressResults =
224+
!isLoading && resultsQuery === normalizedInput;
225+
226+
if (hasCurrentAddressResults && highlightedOption?.type === "mapbox") {
227+
handleAutocompleteOnChange(highlightedOption);
228+
return;
229+
}
230+
231+
const resolvedResults = await ensureMapboxResults(normalizedInput);
232+
233+
if (resolvedResults.length > 0) {
234+
handleAutocompleteOnChange({
235+
type: "mapbox",
236+
value: resolvedResults[0],
237+
});
215238
}
216239
}
217240
};

client/src/hooks/useMapboxGeocoder.js

Lines changed: 103 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@ import {
55
MAPBOX_ACCESS_TOKEN,
66
DEFAULT_VIEWPORTS,
77
} from "helpers/Constants";
8-
import { useCallback, useReducer } from "react";
8+
import { useCallback, useReducer, useRef } from "react";
99

1010
const baseUrl = `https://api.mapbox.com/geocoding/v5/mapbox.places`;
1111

1212
const initialState = {
1313
isLoading: false,
1414
error: false,
1515
mapboxResults: [],
16+
resultsQuery: "",
1617
};
1718

1819
const actionTypes = {
@@ -24,13 +25,14 @@ const actionTypes = {
2425
function reducer(state = initialState, action) {
2526
switch (action.type) {
2627
case actionTypes.FETCH_REQUEST:
27-
return { ...state, isLoading: true };
28+
return { ...state, isLoading: true, resultsQuery: action.query };
2829
case actionTypes.FETCH_SUCCESS:
2930
return {
3031
...state,
3132
error: false,
3233
isLoading: false,
3334
mapboxResults: action.results,
35+
resultsQuery: action.query,
3436
};
3537
case actionTypes.FETCH_FAILURE:
3638
console.error(action.error);
@@ -41,35 +43,112 @@ function reducer(state = initialState, action) {
4143
}
4244

4345
export function useMapboxGeocoder() {
44-
const [{ isLoading, error, mapboxResults }, dispatch] = useReducer(
45-
reducer,
46-
initialState
47-
);
46+
const [{ isLoading, error, mapboxResults, resultsQuery }, dispatch] =
47+
useReducer(reducer, initialState);
48+
const latestQueryRef = useRef("");
49+
50+
const performFetch = useCallback(async (searchString) => {
51+
const bbox = DEFAULT_VIEWPORTS[TENANT_ID].bbox;
52+
const mapboxUrl = `${baseUrl}/${searchString}.json?bbox=${bbox}&access_token=${MAPBOX_ACCESS_TOKEN}`;
53+
54+
try {
55+
const response = await axios.get(mapboxUrl);
56+
const results = response.data.features;
57+
58+
if (latestQueryRef.current === searchString) {
59+
dispatch({
60+
type: actionTypes.FETCH_SUCCESS,
61+
results,
62+
query: searchString,
63+
});
64+
}
65+
66+
return results;
67+
} catch (error) {
68+
if (latestQueryRef.current === searchString) {
69+
dispatch({ type: actionTypes.FETCH_FAILURE, error });
70+
}
71+
return [];
72+
}
73+
}, []);
4874

4975
const debouncedFetch = useCallback(
5076
debounce(
5177
async (searchString) => {
52-
const bbox = DEFAULT_VIEWPORTS[TENANT_ID].bbox;
53-
const mapboxUrl = `${baseUrl}/${searchString}.json?bbox=${bbox}&access_token=${MAPBOX_ACCESS_TOKEN}`;
54-
55-
try {
56-
const response = await axios.get(mapboxUrl);
57-
dispatch({
58-
type: actionTypes.FETCH_SUCCESS,
59-
results: response.data.features,
60-
});
61-
} catch (error) {
62-
dispatch({ type: actionTypes.FETCH_FAILURE, error });
63-
}
78+
await performFetch(searchString);
6479
},
6580
{ wait: 300 }
6681
),
67-
[]
82+
[performFetch]
83+
);
84+
85+
const clearResults = useCallback(() => {
86+
debouncedFetch.cancel?.();
87+
latestQueryRef.current = "";
88+
dispatch({
89+
type: actionTypes.FETCH_SUCCESS,
90+
results: [],
91+
query: "",
92+
});
93+
}, [debouncedFetch]);
94+
95+
const searchMapboxResults = useCallback(
96+
(searchString) => {
97+
const normalizedSearchString = searchString.trim();
98+
latestQueryRef.current = normalizedSearchString;
99+
100+
if (!normalizedSearchString) {
101+
clearResults();
102+
return;
103+
}
104+
105+
dispatch({
106+
type: actionTypes.FETCH_REQUEST,
107+
query: normalizedSearchString,
108+
});
109+
debouncedFetch(normalizedSearchString);
110+
},
111+
[clearResults, debouncedFetch]
112+
);
113+
114+
const ensureMapboxResults = useCallback(
115+
async (searchString) => {
116+
const normalizedSearchString = searchString.trim();
117+
118+
if (!normalizedSearchString) {
119+
clearResults();
120+
return [];
121+
}
122+
123+
if (!isLoading && resultsQuery === normalizedSearchString) {
124+
return mapboxResults;
125+
}
126+
127+
latestQueryRef.current = normalizedSearchString;
128+
debouncedFetch.cancel?.();
129+
dispatch({
130+
type: actionTypes.FETCH_REQUEST,
131+
query: normalizedSearchString,
132+
});
133+
134+
return performFetch(normalizedSearchString);
135+
},
136+
[
137+
clearResults,
138+
debouncedFetch,
139+
isLoading,
140+
mapboxResults,
141+
performFetch,
142+
resultsQuery,
143+
]
68144
);
69-
const fetchMapboxResults = useCallback((searchString) => {
70-
dispatch({ type: actionTypes.FETCH_REQUEST });
71-
debouncedFetch(searchString);
72-
}, []);
73145

74-
return { error, isLoading, mapboxResults, fetchMapboxResults };
146+
return {
147+
error,
148+
isLoading,
149+
mapboxResults,
150+
resultsQuery,
151+
searchMapboxResults,
152+
ensureMapboxResults,
153+
};
75154
}

0 commit comments

Comments
 (0)