Skip to content

Commit 5e205ac

Browse files
committed
fix(map): cancel in-flight fetch when URL loses listingSlug; cleanups
- useMapListingUrl: bump requestTokenRef when listingSlug becomes null so a late fetchBySlug response can't re-select the listing after browser back. - ListingRead: memoize the Supabase client so the thread-loading effect doesn't re-run on every render; drop unused mapZoomLevel state + effect (MapThumbnail already uses initialZoomLevel directly). - ListingChatDrawer: drop the dead listingDisplayName prop (was renamed to a discarded local); simplify isNested check to !isNested so an omitted prop is treated as "not nested". Made-with: Cursor
1 parent 03ff0bd commit 5e205ac

3 files changed

Lines changed: 14 additions & 10 deletions

File tree

src/components/ListingChatDrawer/ListingChatDrawer.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ type ListingChatDrawerProps = {
6565
isChatDrawerOpen: boolean;
6666
setIsChatDrawerOpen: (open: boolean) => void;
6767
existingThread: unknown;
68-
listingDisplayName: string;
6968
};
7069

7170
type SharedDrawerProps = {
@@ -94,12 +93,12 @@ export default function ListingChatDrawer({
9493
isChatDrawerOpen,
9594
setIsChatDrawerOpen,
9695
existingThread,
97-
listingDisplayName: _listingDisplayName,
9896
}: ListingChatDrawerProps) {
9997
const { isDesktop, hasTouch } = useDeviceContext();
10098

10199
// Mobile: always modal. Desktop: modal only if NOT a nested drawer.
102-
const shouldUseModal = !isDesktop || isNested === false;
100+
// (`!isNested` treats an omitted prop the same as `false`.)
101+
const shouldUseModal = !isDesktop || !isNested;
103102

104103
const visibility = listing.visibility ?? undefined;
105104
const isStub = listing.is_stub ?? undefined;

src/components/ListingRead/ListingRead.tsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"use client";
2-
import { Fragment, useState, memo, useEffect } from "react";
2+
import { Fragment, useState, memo, useEffect, useMemo } from "react";
33
import type { ReactNode } from "react";
44
import type { User } from "@supabase/supabase-js";
55

@@ -47,12 +47,17 @@ const ListingRead = memo(function Listing({
4747
const router = useRouter();
4848

4949
const [existingThread, setExistingThread] = useState<unknown>(null);
50-
const [mapZoomLevel, setMapZoomLevel] = useState<number | null>(null);
5150
// Chat drawer state is owned here so that each selected listing gets a
5251
// fresh drawer. The parent resets this by remounting with `key`.
5352
const [isChatDrawerOpen, setIsChatDrawerOpen] = useState(false);
5453

55-
const supabase = presentation !== "demo" ? createClient() : null;
54+
// `createClient()` builds a new Supabase browser client on every call, so
55+
// memoize to keep the reference stable — otherwise the thread-loading
56+
// effect below (which depends on `supabase`) would re-run on every render.
57+
const supabase = useMemo(
58+
() => (presentation !== "demo" ? createClient() : null),
59+
[presentation]
60+
);
5661

5762
const isDemo = presentation === "demo";
5863
const demoListing = isDemoListing(listing) ? listing : null;
@@ -93,9 +98,6 @@ const ListingRead = memo(function Listing({
9398
}, [realListing?.id, user?.id, isDemo, supabase, realListing]);
9499

95100
const initialZoomLevel = 14;
96-
useEffect(() => {
97-
setMapZoomLevel(initialZoomLevel);
98-
}, []);
99101

100102
const listingDisplayName: string = isDemo
101103
? (demoListing?.name ?? demoListing?.owner_first_name ?? "")
@@ -135,7 +137,6 @@ const ListingRead = memo(function Listing({
135137
isChatDrawerOpen={isChatDrawerOpen}
136138
setIsChatDrawerOpen={setIsChatDrawerOpen}
137139
existingThread={existingThread}
138-
listingDisplayName={listingDisplayName}
139140
/>
140141
) : null}
141142

src/features/map/hooks/useMapListingUrl.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,10 @@ export function useMapListingUrl({
155155
// do clear the pin-selection id so the pin snaps back immediately.
156156
useEffect(() => {
157157
if (!listingSlug) {
158+
// Invalidate any in-flight fetch so a late response can't re-select
159+
// the listing the user just navigated away from (e.g. browser Back
160+
// while a deep-link fetch is still in flight).
161+
requestTokenRef.current += 1;
158162
resolvedSlugRef.current = null;
159163
resolvedTableRef.current = null;
160164
setOptimisticListingId(null);

0 commit comments

Comments
 (0)