Skip to content

Commit 4d2eb70

Browse files
committed
change logic so that the filtering of snippets is set based on the URL query param and not the search text, remove the searchVal state and just use the AppContext searchText state instead as it is cleaner, ensure that the search term is preserved when switching categories should one exist
1 parent 1b4e679 commit 4d2eb70

File tree

7 files changed

+70
-53
lines changed

7 files changed

+70
-53
lines changed

src/components/CategoryList.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { FC } from "react";
2-
import { useNavigate } from "react-router-dom";
2+
import { useNavigate, useSearchParams } from "react-router-dom";
33

44
import { useAppContext } from "@contexts/AppContext";
55
import { useCategories } from "@hooks/useCategories";
@@ -11,12 +11,16 @@ interface CategoryListItemProps {
1111

1212
const CategoryListItem: FC<CategoryListItemProps> = ({ name }) => {
1313
const navigate = useNavigate();
14+
const [searchParams] = useSearchParams();
1415

1516
const { language, category, setCategory } = useAppContext();
1617

1718
const handleSelect = () => {
1819
setCategory(name);
19-
navigate(`/${slugify(language.name)}/${slugify(name)}`);
20+
navigate({
21+
pathname: `/${slugify(language.name)}/${slugify(name)}`,
22+
search: searchParams.toString(),
23+
});
2024
};
2125

2226
return (

src/components/LanguageSelector.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,30 @@ import { useAppContext } from "@contexts/AppContext";
99
import { useKeyboardNavigation } from "@hooks/useKeyboardNavigation";
1010
import { useLanguages } from "@hooks/useLanguages";
1111
import { LanguageType } from "@types";
12-
import { configureProfile } from "@utils/configureProfile";
12+
import { configureUserSelection } from "@utils/configureUserSelection";
1313
import { slugify } from "@utils/slugify";
1414

1515
const LanguageSelector = () => {
1616
const navigate = useNavigate();
1717

18-
const { language, setLanguage, setCategory } = useAppContext();
18+
const { language, setLanguage, setCategory, setSearchText } = useAppContext();
1919
const { fetchedLanguages, loading, error } = useLanguages();
2020

2121
const dropdownRef = useRef<HTMLDivElement>(null);
2222
const [isOpen, setIsOpen] = useState(false);
2323

24+
/**
25+
* When setting a new language we need to ensure that a category
26+
* has been set given this new language.
27+
* Ensure that the search text is cleared.
28+
*/
2429
const handleSelect = async (selected: LanguageType) => {
2530
const { language: newLanguage, category: newCategory } =
26-
await configureProfile({
31+
await configureUserSelection({
2732
languageName: selected.name,
2833
});
2934

35+
setSearchText("");
3036
setLanguage(newLanguage);
3137
setCategory(newCategory);
3238
navigate(`/${slugify(newLanguage.name)}/${slugify(newCategory)}`);

src/components/SearchInput.tsx

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useCallback, useEffect, useRef, useState } from "react";
1+
import { useCallback, useEffect, useRef } from "react";
22
import { useSearchParams } from "react-router-dom";
33

44
import { useAppContext } from "@contexts/AppContext";
@@ -13,8 +13,6 @@ const SearchInput = () => {
1313

1414
const inputRef = useRef<HTMLInputElement | null>(null);
1515

16-
const [inputVal, setInputVal] = useState<string>("");
17-
1816
const handleSearchFieldClick = () => {
1917
inputRef.current?.focus();
2018
};
@@ -27,7 +25,6 @@ const SearchInput = () => {
2725
};
2826

2927
const clearSearch = useCallback(() => {
30-
setInputVal("");
3128
setSearchText("");
3229
searchParams.delete(QueryParams.SEARCH);
3330
setSearchParams(searchParams);
@@ -38,6 +35,7 @@ const SearchInput = () => {
3835
if (e.key !== "Escape") {
3936
return;
4037
}
38+
4139
// check if the input is focused
4240
if (document.activeElement !== inputRef.current) {
4341
return;
@@ -55,12 +53,13 @@ const SearchInput = () => {
5553
if (e.key !== "Enter") {
5654
return;
5755
}
56+
5857
// check if the input is focused
5958
if (document.activeElement !== inputRef.current) {
6059
return;
6160
}
6261

63-
const formattedVal = inputVal.trim().toLowerCase();
62+
const formattedVal = searchText.trim().toLowerCase() || "";
6463

6564
setSearchText(formattedVal);
6665
if (!formattedVal) {
@@ -71,7 +70,7 @@ const SearchInput = () => {
7170
setSearchParams(searchParams);
7271
}
7372
},
74-
[inputVal, searchParams, setSearchParams, setSearchText]
73+
[searchParams, searchText, setSearchParams, setSearchText]
7574
);
7675

7776
useEffect(() => {
@@ -87,11 +86,14 @@ const SearchInput = () => {
8786
}, [handleEscapePress, handleReturnPress]);
8887

8988
/**
90-
* Set the input value and search text to the search query from the URL
89+
* Set the search text to the search query from the URL
9190
*/
9291
useEffect(() => {
93-
const searchQuery = searchParams.get(QueryParams.SEARCH) || "";
94-
setInputVal(searchQuery);
92+
const searchQuery = searchParams.get(QueryParams.SEARCH);
93+
if (!searchQuery) {
94+
return;
95+
}
96+
9597
setSearchText(searchQuery);
9698
// eslint-disable-next-line react-hooks/exhaustive-deps
9799
}, []);
@@ -101,26 +103,20 @@ const SearchInput = () => {
101103
<SearchIcon />
102104
<input
103105
ref={inputRef}
106+
value={searchText}
104107
type="search"
105108
id="search"
106109
autoComplete="off"
107-
value={inputVal}
108110
onChange={(e) => {
109111
const newValue = e.target.value;
110112
if (!newValue) {
111113
clearSearch();
112114
return;
113115
}
114-
setInputVal(newValue);
115-
}}
116-
onBlur={() => {
117-
// ensure the input value is always in sync with the search text
118-
if (inputVal !== searchText) {
119-
setInputVal(searchText);
120-
}
116+
setSearchText(newValue);
121117
}}
122118
/>
123-
{!inputVal && !searchText && (
119+
{!searchText && (
124120
<label htmlFor="search">
125121
Type <kbd>/</kbd> to search
126122
</label>

src/contexts/AppContext.tsx

Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,8 @@ import { useParams } from "react-router-dom";
33

44
import { useLanguages } from "@hooks/useLanguages";
55
import { AppState, LanguageType, SnippetType } from "@types";
6-
import { configureProfile } from "@utils/configureProfile";
7-
8-
// TODO: add custom loading and error handling
9-
const defaultState: AppState = {
10-
language: null as unknown as AppState["language"],
11-
setLanguage: () => {},
12-
category: null as unknown as AppState["category"],
13-
setCategory: () => {},
14-
snippet: null,
15-
setSnippet: () => {},
16-
searchText: "",
17-
setSearchText: () => {},
18-
};
6+
import { configureUserSelection } from "@utils/configureUserSelection";
7+
import { defaultState } from "@utils/consts";
198

209
const AppContext = createContext<AppState>(defaultState);
2110

@@ -32,7 +21,7 @@ export const AppProvider: FC<{ children: React.ReactNode }> = ({
3221
const [searchText, setSearchText] = useState<string>("");
3322

3423
const configure = async () => {
35-
const { language, category } = await configureProfile({
24+
const { language, category } = await configureUserSelection({
3625
languageName,
3726
categoryName,
3827
});
@@ -52,16 +41,18 @@ export const AppProvider: FC<{ children: React.ReactNode }> = ({
5241

5342
return (
5443
<AppContext.Provider
55-
value={{
56-
language,
57-
setLanguage: setLanguage as AppState["setLanguage"],
58-
category,
59-
setCategory: setCategory as AppState["setCategory"],
60-
snippet,
61-
setSnippet,
62-
searchText,
63-
setSearchText,
64-
}}
44+
value={
45+
{
46+
language,
47+
setLanguage,
48+
category,
49+
setCategory,
50+
snippet,
51+
setSnippet,
52+
searchText,
53+
setSearchText,
54+
} as AppState
55+
}
6556
>
6657
{children}
6758
</AppContext.Provider>

src/hooks/useSnippets.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
import { useMemo } from "react";
2+
import { useSearchParams } from "react-router-dom";
23

34
import { useAppContext } from "@contexts/AppContext";
45
import { CategoryType } from "@types";
6+
import { QueryParams } from "@utils/enums";
57
import { slugify } from "@utils/slugify";
68

79
import { useFetch } from "./useFetch";
810

911
export const useSnippets = () => {
10-
const { language, category, searchText } = useAppContext();
12+
const [searchParams] = useSearchParams();
13+
14+
const { language, category } = useAppContext();
1115
const { data, loading, error } = useFetch<CategoryType[]>(
1216
`/consolidated/${slugify(language.name)}.json`
1317
);
@@ -17,15 +21,19 @@ export const useSnippets = () => {
1721
return [];
1822
}
1923

20-
if (searchText) {
24+
if (searchParams.has(QueryParams.SEARCH)) {
2125
return data
2226
.find((item) => item.name === category)
2327
?.snippets.filter((item) =>
24-
item.title.toLowerCase().includes(searchText.toLowerCase())
28+
item.title
29+
.toLowerCase()
30+
.includes(
31+
(searchParams.get(QueryParams.SEARCH) || "").toLowerCase()
32+
)
2533
);
2634
}
2735
return data.find((item) => item.name === category)?.snippets;
28-
}, [category, data, searchText]);
36+
}, [category, data, searchParams]);
2937

3038
return { fetchedSnippets, loading, error };
3139
};

src/utils/configureProfile.ts renamed to src/utils/configureUserSelection.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { CategoryType, LanguageType } from "@types";
33
import { defaultCategory, defaultLanguage } from "./consts";
44
import { slugify } from "./slugify";
55

6-
export async function configureProfile({
6+
export async function configureUserSelection({
77
languageName,
88
categoryName,
99
}: {

src/utils/consts.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { CategoryType, LanguageType } from "@types";
1+
import { AppState, CategoryType, LanguageType } from "@types";
22

33
export const defaultLanguage: LanguageType = {
44
name: "JAVASCRIPT",
@@ -10,3 +10,15 @@ export const defaultCategory: CategoryType = {
1010
name: "",
1111
snippets: [],
1212
};
13+
14+
// TODO: add custom loading and error handling
15+
export const defaultState: AppState = {
16+
language: defaultLanguage,
17+
setLanguage: () => {},
18+
category: defaultCategory.name,
19+
setCategory: () => {},
20+
snippet: null,
21+
setSnippet: () => {},
22+
searchText: "",
23+
setSearchText: () => {},
24+
};

0 commit comments

Comments
 (0)