Skip to content

Commit 26adc39

Browse files
committed
hydration mismatch fix: prevent default feed flashing when arriving via url param
1 parent b6576e5 commit 26adc39

File tree

1 file changed

+79
-26
lines changed

1 file changed

+79
-26
lines changed

src/features/feeds/components/FeedList.tsx

Lines changed: 79 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,18 @@ export const FeedList = ({
4747
const isSmartData = dataFeedType === "smartdata"
4848
const isUSGovernmentMacroeconomicData = dataFeedType === "usGovernmentMacroeconomicData"
4949

50-
// Get network directly from URL
51-
const networkFromURL =
52-
typeof window !== "undefined" ? new URLSearchParams(window.location.search).get("network") : null
50+
// Get network directly from URL with better SSR handling
51+
const getInitialNetwork = () => {
52+
if (typeof window === "undefined") return initialNetwork
53+
const params = new URLSearchParams(window.location.search)
54+
const networkParam = params.get("network")
55+
return networkParam || initialNetwork
56+
}
57+
58+
const networkFromURL = getInitialNetwork()
5359

5460
// If URL has a network param, use it directly
55-
const effectiveInitialNetwork = networkFromURL || initialNetwork
61+
const effectiveInitialNetwork = networkFromURL
5662

5763
// Initialize state with the URL value
5864
const [currentNetwork, setCurrentNetwork] = useState(effectiveInitialNetwork)
@@ -87,27 +93,46 @@ export const FeedList = ({
8793
}
8894
}, [currentNetwork, isStreams])
8995

90-
// Force initial sync with URL
96+
// Force initial sync with URL - handle hydration mismatches
9197
useEffect(() => {
92-
// Get the latest network from URL
93-
const latestNetworkFromURL = getNetworkFromURL()
94-
if (latestNetworkFromURL !== currentNetwork) {
95-
setCurrentNetwork(latestNetworkFromURL)
98+
// Only run this effect on the client side after mount
99+
if (typeof window !== "undefined") {
100+
const latestNetworkFromURL = getNetworkFromURL()
101+
if (latestNetworkFromURL !== currentNetwork) {
102+
setCurrentNetwork(latestNetworkFromURL)
103+
}
96104
}
105+
}, []) // Run only once on mount
97106

98-
// Force a redraw after a short delay
107+
// Additional sync for when window loads (fallback)
108+
useEffect(() => {
99109
if (typeof window !== "undefined") {
100-
// execute after the DOM is fully loaded
101-
window.addEventListener("load", () => {
110+
const handleLoad = () => {
102111
const networkFromURL = getNetworkFromURL()
103-
setCurrentNetwork(networkFromURL)
104-
// Note: network button updates removed since we now use dropdown selector
105-
})
112+
if (networkFromURL !== currentNetwork) {
113+
setCurrentNetwork(networkFromURL)
114+
}
115+
}
116+
117+
// If window is already loaded, run immediately
118+
if (document.readyState === 'complete') {
119+
handleLoad()
120+
} else {
121+
window.addEventListener("load", handleLoad)
122+
return () => window.removeEventListener("load", handleLoad)
123+
}
106124
}
107125
}, [])
108126

109127
// Track the selected network type (mainnet/testnet)
110128
const [selectedNetworkType, setSelectedNetworkType] = useState<"mainnet" | "testnet">("mainnet")
129+
130+
// Track hydration to prevent SSR mismatch
131+
const [isHydrated, setIsHydrated] = useState(false)
132+
133+
useEffect(() => {
134+
setIsHydrated(true)
135+
}, [])
111136

112137
// Regular query string states
113138
const [searchValue, setSearchValue] = useQueryString("search", "")
@@ -163,7 +188,32 @@ export const FeedList = ({
163188
]
164189
const [streamsChain] = useState(initialNetwork)
165190
const activeChain = isStreams ? streamsChain : currentNetwork
166-
const chain = chains.find((c) => c.page === activeChain) || chains[0]
191+
192+
// More robust chain finding - ensure we have a valid chain
193+
const chain = useMemo(() => {
194+
// During SSR, if we don't have an activeChain but we have a network param in the URL,
195+
// try to find the chain directly from the URL param to prevent hydration mismatch
196+
if (!activeChain) {
197+
// Check if we have a network param that we can use directly
198+
if (typeof window !== "undefined") {
199+
const urlParams = new URLSearchParams(window.location.search)
200+
const networkParam = urlParams.get("network")
201+
if (networkParam) {
202+
const foundFromUrl = chains.find((c) => c.page === networkParam)
203+
if (foundFromUrl) {
204+
return foundFromUrl
205+
}
206+
}
207+
}
208+
return chains[0] // fallback only if no activeChain
209+
}
210+
211+
const foundChain = chains.find((c) => c.page === activeChain)
212+
if (!foundChain) {
213+
return chains[0]
214+
}
215+
return foundChain
216+
}, [activeChain, chains])
167217
const chainMetadata = useGetChainMetadata(chain, initialCache && initialCache[chain.page])
168218
const wrapperRef = useRef(null)
169219

@@ -644,16 +694,19 @@ export const FeedList = ({
644694
flexWrap: "nowrap"
645695
}}
646696
>
647-
<ChainSelector
648-
chains={chains}
649-
selectedChain={chain}
650-
currentNetwork={currentNetwork}
651-
onChainSelect={handleNetworkSelect}
652-
onNetworkTypeChange={handleNetworkTypeChange}
653-
dataFeedType={dataFeedType}
654-
availableNetworkTypes={availableNetworkTypes}
655-
selectedNetworkType={selectedNetworkType}
656-
/>
697+
{isHydrated && (
698+
<ChainSelector
699+
key={`chain-selector-${chain.page}`} // Force re-render when chain changes
700+
chains={chains}
701+
selectedChain={chain}
702+
currentNetwork={currentNetwork}
703+
onChainSelect={handleNetworkSelect}
704+
onNetworkTypeChange={handleNetworkTypeChange}
705+
dataFeedType={dataFeedType}
706+
availableNetworkTypes={availableNetworkTypes}
707+
selectedNetworkType={selectedNetworkType}
708+
/>
709+
)}
657710
</div>
658711
</>
659712
)}

0 commit comments

Comments
 (0)