Skip to content

Commit 6b50aa3

Browse files
committed
fix race condition with updating url with search params on intitial load
1 parent a2402d4 commit 6b50aa3

File tree

5 files changed

+84
-131
lines changed

5 files changed

+84
-131
lines changed

src/components/ChangelogFilters/DesktopFilters.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ export const DesktopFilters = ({ products, networks, types, items }: DesktopFilt
134134
clearProductFilters,
135135
clearNetworkFilters,
136136
clearTypeFilters,
137-
} = useChangelogFilters({ products, networks, types, items })
137+
} = useChangelogFilters({ items })
138138

139139
const toggleFilter = (filterType: FilterType) => {
140140
setActiveFilter(filterType)

src/components/ChangelogFilters/MobileFilters.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ export const MobileFilters = ({ products, networks, types, items }: MobileFilter
271271
clearNetworkFilters,
272272
clearTypeFilters,
273273
clearAllFilters,
274-
} = useChangelogFilters({ products, networks, types, items })
274+
} = useChangelogFilters({ items })
275275

276276
const totalFilterCount = selectedProducts.length + selectedNetworks.length + selectedTypes.length
277277

src/components/ChangelogFilters/useChangelogFilters.ts

Lines changed: 77 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,62 @@
1-
import { useState, useEffect } from "react"
1+
import { useState, useEffect, useRef } from "react"
22
import type { ChangelogItem } from "~/components/ChangelogSnippet/types.ts"
33
import { matchesFilters } from "~/utils/changelogFilters.ts"
44
import { parseURLParams, updateFilterURL, toggleItemInArray } from "~/utils/changelogFilterUtils.ts"
55

66
export interface UseChangelogFiltersProps {
7-
products: string[]
8-
networks: string[]
9-
types: string[]
107
items: ChangelogItem[]
118
}
129

13-
export const useChangelogFilters = ({ products, networks, types, items }: UseChangelogFiltersProps) => {
14-
const [searchExpanded, setSearchExpanded] = useState(false)
15-
const [searchTerm, setSearchTerm] = useState("")
16-
const [selectedProducts, setSelectedProducts] = useState<string[]>([])
17-
const [selectedNetworks, setSelectedNetworks] = useState<string[]>([])
18-
const [selectedTypes, setSelectedTypes] = useState<string[]>([])
10+
interface FilterState {
11+
selectedProducts: string[]
12+
selectedNetworks: string[]
13+
selectedTypes: string[]
14+
searchTerm: string
15+
searchExpanded: boolean
16+
}
17+
18+
export const useChangelogFilters = ({ items }: UseChangelogFiltersProps) => {
19+
const [filters, setFilters] = useState<FilterState>({
20+
selectedProducts: [],
21+
selectedNetworks: [],
22+
selectedTypes: [],
23+
searchTerm: "",
24+
searchExpanded: false,
25+
})
26+
const isInitialMount = useRef(true)
27+
const hasLoadedFromURL = useRef(false)
1928

2029
// Read URL parameters on mount
2130
useEffect(() => {
2231
const urlParams = parseURLParams()
2332

24-
setSelectedProducts(urlParams.products)
25-
setSelectedNetworks(urlParams.networks)
26-
setSelectedTypes(urlParams.types)
27-
setSearchTerm(urlParams.searchTerm)
28-
setSearchExpanded(urlParams.searchExpanded)
33+
setFilters({
34+
selectedProducts: urlParams.products,
35+
selectedNetworks: urlParams.networks,
36+
selectedTypes: urlParams.types,
37+
searchTerm: urlParams.searchTerm,
38+
searchExpanded: urlParams.searchExpanded,
39+
})
40+
41+
hasLoadedFromURL.current = true
2942
}, [])
3043

31-
// Update URL when filters change
44+
// Update URL when filters change (but not on initial mount)
3245
useEffect(() => {
33-
updateFilterURL(selectedProducts, selectedNetworks, selectedTypes, searchTerm)
34-
}, [selectedProducts, selectedNetworks, selectedTypes, searchTerm])
46+
// Skip the first render and the initial load from URL
47+
if (isInitialMount.current) {
48+
isInitialMount.current = false
49+
return
50+
}
51+
52+
// Skip if we just loaded from URL
53+
if (hasLoadedFromURL.current) {
54+
hasLoadedFromURL.current = false
55+
return
56+
}
57+
58+
updateFilterURL(filters.selectedProducts, filters.selectedNetworks, filters.selectedTypes, filters.searchTerm)
59+
}, [filters])
3560

3661
// Filter items and update the display
3762
useEffect(() => {
@@ -43,9 +68,9 @@ export const useChangelogFilters = ({ products, networks, types, items }: UseCha
4368
const emptyState = document.querySelector(".empty-state") as HTMLElement
4469
const changelogList = document.querySelector(".changelog-list") as HTMLElement
4570

46-
if (searchTerm) {
71+
if (filters.searchTerm) {
4772
// Search takes priority - filter by search term
48-
const searchLower = searchTerm.toLowerCase()
73+
const searchLower = filters.searchTerm.toLowerCase()
4974
let visibleCount = 0
5075

5176
changelogItems.forEach((item) => {
@@ -82,14 +107,20 @@ export const useChangelogFilters = ({ products, networks, types, items }: UseCha
82107
} else {
83108
// Apply filter logic
84109
let visibleCount = 0
85-
const hasFilters = selectedProducts.length > 0 || selectedNetworks.length > 0 || selectedTypes.length > 0
110+
const hasFilters =
111+
filters.selectedProducts.length > 0 || filters.selectedNetworks.length > 0 || filters.selectedTypes.length > 0
86112

87113
changelogItems.forEach((item) => {
88114
const index = parseInt(item.getAttribute("data-index") || "0")
89115
const changelogItem = items[index]
90116

91117
if (hasFilters && changelogItem) {
92-
const matches = matchesFilters(changelogItem, selectedProducts, selectedNetworks, selectedTypes)
118+
const matches = matchesFilters(
119+
changelogItem,
120+
filters.selectedProducts,
121+
filters.selectedNetworks,
122+
filters.selectedTypes
123+
)
93124
if (matches) {
94125
;(item as HTMLElement).style.display = ""
95126
visibleCount++
@@ -132,54 +163,53 @@ export const useChangelogFilters = ({ products, networks, types, items }: UseCha
132163
}
133164
}
134165
}
135-
}, [searchTerm, selectedProducts, selectedNetworks, selectedTypes, items])
166+
}, [filters, items])
136167

137168
const handleSearchChange = (value: string) => {
138-
setSearchTerm(value)
169+
setFilters((prev) => ({ ...prev, searchTerm: value }))
139170
}
140171

141172
const handleSearchToggle = (expanded: boolean) => {
142-
setSearchExpanded(expanded)
173+
setFilters((prev) => ({ ...prev, searchExpanded: expanded }))
143174
}
144175

145176
const toggleSelection = (type: "product" | "network" | "type", value: string) => {
146-
switch (type) {
147-
case "product":
148-
setSelectedProducts((prev) => toggleItemInArray(prev, value))
149-
break
150-
case "network":
151-
setSelectedNetworks((prev) => toggleItemInArray(prev, value))
152-
break
153-
case "type":
154-
setSelectedTypes((prev) => toggleItemInArray(prev, value))
155-
break
156-
}
177+
setFilters((prev) => {
178+
switch (type) {
179+
case "product":
180+
return { ...prev, selectedProducts: toggleItemInArray(prev.selectedProducts, value) }
181+
case "network":
182+
return { ...prev, selectedNetworks: toggleItemInArray(prev.selectedNetworks, value) }
183+
case "type":
184+
return { ...prev, selectedTypes: toggleItemInArray(prev.selectedTypes, value) }
185+
default:
186+
return prev
187+
}
188+
})
157189
}
158190

159191
const clearProductFilters = () => {
160-
setSelectedProducts([])
192+
setFilters((prev) => ({ ...prev, selectedProducts: [] }))
161193
}
162194

163195
const clearNetworkFilters = () => {
164-
setSelectedNetworks([])
196+
setFilters((prev) => ({ ...prev, selectedNetworks: [] }))
165197
}
166198

167199
const clearTypeFilters = () => {
168-
setSelectedTypes([])
200+
setFilters((prev) => ({ ...prev, selectedTypes: [] }))
169201
}
170202

171203
const clearAllFilters = () => {
172-
setSelectedProducts([])
173-
setSelectedNetworks([])
174-
setSelectedTypes([])
204+
setFilters((prev) => ({ ...prev, selectedProducts: [], selectedNetworks: [], selectedTypes: [] }))
175205
}
176206

177207
return {
178-
searchExpanded,
179-
searchTerm,
180-
selectedProducts,
181-
selectedNetworks,
182-
selectedTypes,
208+
searchExpanded: filters.searchExpanded,
209+
searchTerm: filters.searchTerm,
210+
selectedProducts: filters.selectedProducts,
211+
selectedNetworks: filters.selectedNetworks,
212+
selectedTypes: filters.selectedTypes,
183213
handleSearchChange,
184214
handleSearchToggle,
185215
toggleSelection,

src/hooks/useQueryStringReact.ts

Lines changed: 0 additions & 77 deletions
This file was deleted.

src/utils/changelogFilters.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,21 +70,21 @@ export function getUniqueTypes(items: ChangelogItem[]): string[] {
7070
*/
7171
export function matchesFilters(
7272
item: ChangelogItem,
73-
selectedTopics: string[],
73+
selectedProducts: string[],
7474
selectedNetworks: string[],
7575
selectedTypes: string[]
7676
): boolean {
7777
// If no filters selected, show all items
78-
const hasTopicFilter = selectedTopics.length > 0
78+
const hasProductFilter = selectedProducts.length > 0
7979
const hasNetworkFilter = selectedNetworks.length > 0
8080
const hasTypeFilter = selectedTypes.length > 0
8181

82-
if (!hasTopicFilter && !hasNetworkFilter && !hasTypeFilter) {
82+
if (!hasProductFilter && !hasNetworkFilter && !hasTypeFilter) {
8383
return true
8484
}
8585

86-
// Check topic filter
87-
if (hasTopicFilter && !selectedTopics.includes(item.topic)) {
86+
// Check product filter (matches against item.topic field)
87+
if (hasProductFilter && !selectedProducts.includes(item.topic)) {
8888
return false
8989
}
9090

0 commit comments

Comments
 (0)