Skip to content

fix: Redux State Isolation - Prevent Gallery Pollution from Cluster View#761

Closed
Jitmisra wants to merge 1 commit intoAOSSIE-Org:mainfrom
Jitmisra:fix/redux-state-isolation-706
Closed

fix: Redux State Isolation - Prevent Gallery Pollution from Cluster View#761
Jitmisra wants to merge 1 commit intoAOSSIE-Org:mainfrom
Jitmisra:fix/redux-state-isolation-706

Conversation

@Jitmisra
Copy link
Copy Markdown

@Jitmisra Jitmisra commented Dec 13, 2025

#706

fix: Redux State Isolation - Prevent Gallery Pollution from Cluster View

🎯 Resolves Issue #706

Fixes Redux state pollution where viewing a person's face cluster overwrites the global image gallery, causing incorrect Home page display after navigation.


🐛 Problem

Current Bug Behavior

1. Home page loads full gallery (100 images) ✅
2. Navigate to /person/<clusterId> → shows 5 cluster images ✅
3. Navigate back to Home (browser back or navbar) ❌
4. Home gallery now shows only those 5 images ❌
5. Hard refresh required to restore full gallery ❌

Root Cause

File: frontend/src/pages/PersonImages/PersonImages.tsx

dispatch(setImages(images));  // ❌ Overwrites GLOBAL images state!

This overwrites the global Redux imagesSlice which is shared by Home page, Search, and other views.


🔧 Solution: Source Tracking

Added a source field to track where images came from. Home page detects stale data and auto-refetches.

Architecture

┌─────────────────────────────────────────────────────────────────┐
│                        BEFORE (Broken)                          │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   [Home Page]              [Person View]                        │
│       │                         │                               │
│       │ reads                   │ overwrites                    │
│       ▼                         ▼                               │
│   ┌─────────────────────────────────────────┐                   │
│   │         imagesSlice (SHARED)            │                   │
│   │  images: [100 images] → [5 images]      │ ← CORRUPTED!      │
│   └─────────────────────────────────────────┘                   │
│                                                                 │
│   Result: Home shows 5 images after returning ❌                │
└─────────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────┐
│                        AFTER (Fixed)                            │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   [Home Page]              [Person View]                        │
│       │                         │                               │
│       │ setGalleryImages()      │ setClusterImages()            │
│       ▼                         ▼                               │
│   ┌─────────────────────────────────────────┐                   │
│   │         imagesSlice (WITH SOURCE)       │                   │
│   │  images: [...]                          │                   │
│   │  source: 'gallery' | 'cluster'  ← TRACKED                   │
│   └─────────────────────────────────────────┘                   │
│                         │                                       │
│                         ▼                                       │
│   Home detects: source !== 'gallery' → Auto-refetch! ✅         │
│                                                                 │
│   Result: Home always shows full gallery ✅                     │
└─────────────────────────────────────────────────────────────────┘

State Flow

    ┌─────────────┐         ┌─────────────┐         ┌─────────────┐
    │   HOME      │────────►│   PERSON    │────────►│    HOME     │
    │  (Start)    │         │    VIEW     │         │  (Return)   │
    └─────────────┘         └─────────────┘         └─────────────┘
          │                       │                       │
          ▼                       ▼                       ▼
    ┌─────────────┐         ┌─────────────┐         ┌─────────────┐
    │ source:     │         │ source:     │         │ needsRefresh│
    │ 'gallery'   │         │ 'cluster'   │         │ = true      │
    └─────────────┘         └─────────────┘         └──────┬──────┘
                                                           │
                                                           ▼
                                                    ┌─────────────┐
                                                    │ refetch()   │
                                                    │ gallery ✅  │
                                                    └─────────────┘

💻 Implementation

New State Structure

export type ImageSource = 'gallery' | 'cluster' | 'search' | 'album' | null;

interface ImageState {
  images: Image[];
  currentViewIndex: number;
  source: ImageSource;  // ✨ Tracks where images came from
}

New Actions

setGalleryImages(images)   source: 'gallery'
setClusterImages(images)   source: 'cluster'
setSearchImages(images)    source: 'search'

Auto-Recovery in Home

const needsRefresh = useSelector(selectNeedsGalleryRefresh);

useEffect(() => {
  if (!isSearchActive && needsRefresh) {
    refetch();  // Auto-refetch when returning from cluster view
  }
}, [needsRefresh, isSearchActive, refetch]);

✅ Files Changed

File Change
frontend/src/features/imageSlice.ts Added source field + typed actions
frontend/src/features/imageSelectors.ts Added selectNeedsGalleryRefresh
frontend/src/pages/Home/Home.tsx Auto-refetch on stale data
frontend/src/pages/PersonImages/PersonImages.tsx Use setClusterImages
frontend/src/features/__tests__/imageSlice.test.ts Unit tests
frontend/src/features/__tests__/imageSelectors.test.ts Unit tests

🧪 Testing

Unit Tests: 23 Passing

npm test -- --testPathPattern="imageSlice|imageSelectors"

 PASS  src/features/__tests__/imageSelectors.test.ts
 PASS  src/features/__tests__/imageSlice.test.ts

Tests: 23 passed

Manual Test Steps

  1. Load Home → verify full gallery (100 images)
  2. Navigate to Person view → verify cluster images (5)
  3. Navigate back → verify full gallery restored ✅
  4. No hard refresh needed ✅

📋 Checklist

  • Lint check passes
  • Unit tests added (23 tests)
  • All tests pass
  • Code formatting correct
  • No breaking changes

Summary by CodeRabbit

Release Notes

  • New Features

    • Images now track their source (gallery, cluster, search, or album) for improved state management.
    • Gallery automatically refreshes when returning from cluster view to ensure up-to-date content.
  • Tests

    • Added unit tests for image selector logic.
    • Added unit tests for image state management.

✏️ Tip: You can customize this high-level summary in your review settings.

…iew (AOSSIE-Org#706)

Problem:
- Navigating to person/cluster view overwrites global image state
- Returning to Home shows only cluster images instead of full gallery
- Required hard refresh to restore gallery

Solution:
- Add 'source' field to imageSlice to track where images came from
- Add setGalleryImages, setClusterImages, setSearchImages actions
- Home page detects stale data (source !== 'gallery') and auto-refetches
- PersonImages uses setClusterImages to mark source as 'cluster'

Benefits over alternative approaches:
- No duplicate state (single source of truth)
- Minimal code changes (4 files modified)
- Extensible for future views (albums, tags, etc.)
- Auto-recovery without manual cleanup

Tests: 23 unit tests added and passing

Closes AOSSIE-Org#706
@github-actions github-actions bot added the needs-triage The maintainer needs time to review this issue. Please do not begin working on it. label Dec 13, 2025
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Dec 13, 2025

Walkthrough

The PR introduces image source tracking to Redux state via a new ImageSource type, adds source-specific reducers (setGalleryImages, setClusterImages, setSearchImages), and implements stale data detection through a new selectNeedsGalleryRefresh selector. Home component now automatically refetches gallery data when navigating from other views.

Changes

Cohort / File(s) Change Summary
Redux State Management
frontend/src/features/imageSlice.ts
Introduces ImageSource type, adds source field to image state, creates source-specific reducers (setGalleryImages, setClusterImages, setSearchImages), deprecates setImages, resets source on clearImages
Redux Selectors
frontend/src/features/imageSelectors.ts
Adds selectImageSource selector and memoized selectNeedsGalleryRefresh selector that returns true when source is not 'gallery' or null
Component Updates
frontend/src/pages/Home/Home.tsx
Replaces setImages with setGalleryImages, adds selectNeedsGalleryRefresh hook, implements refetch effect on stale data detection, extends useMutationFeedback with user-facing messages
Component Updates
frontend/src/pages/PersonImages/PersonImages.tsx
Replaces setImages with setClusterImages when loading cluster images
Test Suites
frontend/src/features/__tests__/imageSlice.test.ts, frontend/src/features/__tests__/imageSelectors.test.ts
Comprehensive unit tests covering all new reducers, selectors, state transitions, source isolation, and Issue #706 refresh scenarios

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Verify the refetch effect logic in Home.tsx correctly triggers only when appropriate (needsRefresh true, not in search mode)
  • Confirm all image source transitions are properly handled across components
  • Validate test coverage adequately exercises state isolation and source-specific behaviors
  • Check that selectNeedsGalleryRefresh logic correctly identifies stale gallery data from cluster/search sources

Possibly related PRs

  • Fix/redux state simplification #599: Modifies the same Redux image state files (imageSlice.ts, imageSelectors.ts), directly related through overlapping changes to exported actions and selectors

Suggested labels

frontend

Poem

🐰 Hop, hop—images now know their source!
From cluster to gallery, refresh runs its course,
State tracks where data belongs with pride,
No stale views linger when users navigate wide!

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main fix: preventing gallery data pollution when returning from cluster view by implementing Redux state isolation via a source field.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (5)
frontend/src/features/imageSelectors.ts (1)

3-15: Use a type-only import for ImageSource to avoid runtime import coupling.

In TS, prefer:

-import { ImageSource } from './imageSlice';
+import type { ImageSource } from './imageSlice';
frontend/src/pages/PersonImages/PersonImages.tsx (1)

41-47: Avoid any + avoid shadowing images in the success branch.

Right now const res: any and const images = ... (Line 41-42) makes this easier to misuse. Consider:

-      const res: any = data?.data;
-      const images = (res?.images || []) as Image[];
+      const res = data?.data as { images?: Image[]; cluster_name?: string } | undefined;
+      const clusterImages = res?.images ?? [];
       // Use setClusterImages to mark source as 'cluster'
       // This allows Home page to detect stale data and refetch
-      dispatch(setClusterImages(images));
+      dispatch(setClusterImages(clusterImages));
frontend/src/features/__tests__/imageSlice.test.ts (1)

129-140: Prefer asserting via selectNeedsGalleryRefresh (avoids duplicating logic in tests).

This test hardcodes source !== 'gallery'; it’ll drift if the selector changes (e.g., null handling). Consider importing and asserting the selector instead.

frontend/src/features/__tests__/imageSelectors.test.ts (1)

11-24: Tighten mock typing: avoid any[] in createMockState.

If feasible, use Image[] (or minimal {id: string}[]) to keep selector tests from accepting invalid shapes silently.

frontend/src/features/imageSlice.ts (1)

8-8: Minor: ImageSource includes 'album' but there’s no setAlbumImages yet.

Either add setAlbumImages for completeness, or remove 'album' until it’s supported (to avoid “dead” source values showing up via selectNeedsGalleryRefresh).

Also applies to: 47-52

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d07d817 and 7864578.

📒 Files selected for processing (6)
  • frontend/src/features/__tests__/imageSelectors.test.ts (1 hunks)
  • frontend/src/features/__tests__/imageSlice.test.ts (1 hunks)
  • frontend/src/features/imageSelectors.ts (2 hunks)
  • frontend/src/features/imageSlice.ts (2 hunks)
  • frontend/src/pages/Home/Home.tsx (3 hunks)
  • frontend/src/pages/PersonImages/PersonImages.tsx (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
frontend/src/features/__tests__/imageSelectors.test.ts (3)
frontend/src/features/imageSlice.ts (1)
  • ImageSource (8-8)
frontend/src/app/store.ts (1)
  • RootState (22-22)
frontend/src/features/imageSelectors.ts (5)
  • selectImages (6-8)
  • selectCurrentViewIndex (10-11)
  • selectImageSource (13-14)
  • selectIsImageViewOpen (17-20)
  • selectNeedsGalleryRefresh (26-29)
frontend/src/features/imageSlice.ts (1)
frontend/src/types/Media.ts (1)
  • Image (13-23)
frontend/src/features/imageSelectors.ts (2)
frontend/src/app/store.ts (1)
  • RootState (22-22)
frontend/src/features/imageSlice.ts (1)
  • ImageSource (8-8)
frontend/src/pages/PersonImages/PersonImages.tsx (1)
frontend/src/features/imageSlice.ts (1)
  • setClusterImages (42-45)
🔇 Additional comments (5)
frontend/src/pages/PersonImages/PersonImages.tsx (1)

6-6: Correct action for isolation: setClusterImages instead of overwriting gallery state.

frontend/src/features/__tests__/imageSlice.test.ts (1)

25-79: Solid reducer coverage for new source field + actions.

frontend/src/pages/Home/Home.tsx (1)

9-13: setGalleryImages wiring looks correct and matches the new source-tracking model.

Also applies to: 54-59

frontend/src/features/__tests__/imageSelectors.test.ts (1)

25-120: Selector test coverage is comprehensive for the new “source/refresh” behavior.

frontend/src/features/imageSlice.ts (1)

4-21: Source-tracking model is a clean, minimal fix for state pollution.

Also applies to: 35-52

Comment on lines +22 to +29
/**
* Returns true if the current images are NOT from the gallery
* Used to trigger refetch when returning to Home page
*/
export const selectNeedsGalleryRefresh = createSelector(
[selectImageSource],
(source) => source !== null && source !== 'gallery',
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Selector logic is good; docstring doesn’t match behavior (“cluster/search” vs includes “album”).

selectNeedsGalleryRefresh returns true for any non-null non-gallery source (including 'album'). Consider updating the comment to match the implementation (or constrain the logic if album shouldn’t trigger refresh).

🤖 Prompt for AI Agents
In frontend/src/features/imageSelectors.ts around lines 22 to 29, the docstring
claims the selector returns true only when images are NOT from the gallery
("cluster/search"), but the implementation returns true for any non-null,
non-'gallery' source (including 'album'); either update the comment to describe
that it triggers refresh for any non-gallery source (e.g., cluster, search,
album) or change the predicate to explicitly exclude album as well (e.g., source
!== null && source !== 'gallery' && source !== 'album') depending on intended
behavior so the comment and logic are consistent.

Comment on lines +27 to 34
/**
* Sets images with source tracking
* @deprecated Use setGalleryImages or setClusterImages instead for clarity
*/
setImages(state, action: PayloadAction<Image[]>) {
state.images = action.payload;
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's look at the imageSlice.ts file to understand the full context
cat -n frontend/src/features/imageSlice.ts | head -80

Repository: AOSSIE-Org/PictoPy

Length of output: 2688


🏁 Script executed:

# Search for setImages usage in the codebase
rg -n --type=ts --type=tsx "\bsetImages\s*\(" frontend/src -S -A 2 -B 2

Repository: AOSSIE-Org/PictoPy

Length of output: 89


🏁 Script executed:

# Search for dispatch of setImages
rg -n --type=ts --type=tsx "dispatch\(\s*setImages" frontend/src -S -A 1 -B 1

Repository: AOSSIE-Org/PictoPy

Length of output: 89


🏁 Script executed:

# Look for how 'source' field is used in the state and refresh detection
rg -n --type=ts --type=tsx "\bsource\b" frontend/src/features/imageSlice.ts -A 2 -B 2

Repository: AOSSIE-Org/PictoPy

Length of output: 89


🏁 Script executed:

# Search for setImages usage with corrected flags
rg -n "setImages\s*\(" frontend/src -A 2 -B 2

Repository: AOSSIE-Org/PictoPy

Length of output: 3309


🏁 Script executed:

# Search for dispatch of setImages
rg -n "dispatch\(\s*setImages" frontend/src -A 1 -B 1

Repository: AOSSIE-Org/PictoPy

Length of output: 1274


🏁 Script executed:

# Look for how 'source' field is used in refresh detection
rg -n "source.*gallery\|gallery.*source" frontend/src -A 2 -B 2

Repository: AOSSIE-Org/PictoPy

Length of output: 44


🏁 Script executed:

# Check Home component for refresh detection logic
fd Home frontend/src -type f

Repository: AOSSIE-Org/PictoPy

Length of output: 232


🏁 Script executed:

# Find Home component
fd -e tsx -e ts Home frontend/src

Repository: AOSSIE-Org/PictoPy

Length of output: 94


🏁 Script executed:

# Look at the Home component(s)
rg -n "source\s*===.*gallery\|gallery.*source" frontend/src -A 3 -B 3

Repository: AOSSIE-Org/PictoPy

Length of output: 44


🏁 Script executed:

# Check the context of each setImages call to understand what they should be
cat -n frontend/src/pages/Home/MyFav.tsx | head -60

Repository: AOSSIE-Org/PictoPy

Length of output: 2574


🏁 Script executed:

# Check AITagging context
cat -n frontend/src/pages/AITagging/AITagging.tsx | head -50

Repository: AOSSIE-Org/PictoPy

Length of output: 2287


🏁 Script executed:

# Check WebCam context
cat -n frontend/src/components/WebCam/WebCamComponent.tsx | head -65

Repository: AOSSIE-Org/PictoPy

Length of output: 2624


🏁 Script executed:

# Look at Home.tsx for any refresh/source-related logic
cat -n frontend/src/pages/Home/Home.tsx | head -100

Repository: AOSSIE-Org/PictoPy

Length of output: 3733


🏁 Script executed:

# Search for any refresh or skip logic based on source
rg -n "source\s*===" frontend/src -A 2 -B 2

Repository: AOSSIE-Org/PictoPy

Length of output: 44


🏁 Script executed:

# Check if there's any conditional logic based on source in selectors or effects
rg -n "source" frontend/src/features/imageSelectors.ts -A 2 -B 2

Repository: AOSSIE-Org/PictoPy

Length of output: 344


setImages (legacy) actively pollutes source field and breaks refresh detection—must be fixed or fully replaced.

Found 5 active dispatches of setImages across MyFav, AITagging, WebCamComponent, and FaceSearchDialog. The reducer does not update source, allowing stale values to persist. This directly breaks selectNeedsGalleryRefresh (imageSelectors.ts, line 28), which relies on source to trigger gallery refetch in Home:

  • selectNeedsGalleryRefresh returns source !== null && source !== 'gallery'
  • If a non-gallery flow (AITagging, face search) calls setImages without resetting source, the next Home navigation may skip necessary refresh.

The test file (imageSlice.test.ts, lines 52–60) explicitly documents this behavior as intentional—setImages preserves the existing source value.

Recommended fix: Reset source in setImages to force Home to recognize the state as non-gallery:

    setImages(state, action: PayloadAction<Image[]>) {
      state.images = action.payload;
+     state.source = null; // unknown/legacy: forces Home refresh detection
    },

Alternatively, migrate all 5 call sites to use setGalleryImages, setClusterImages, or setSearchImages (depending on context) and deprecate setImages entirely.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/**
* Sets images with source tracking
* @deprecated Use setGalleryImages or setClusterImages instead for clarity
*/
setImages(state, action: PayloadAction<Image[]>) {
state.images = action.payload;
},
/**
* Sets images with source tracking
* @deprecated Use setGalleryImages or setClusterImages instead for clarity
*/
setImages(state, action: PayloadAction<Image[]>) {
state.images = action.payload;
state.source = null; // unknown/legacy: forces Home refresh detection
},
🤖 Prompt for AI Agents
In frontend/src/features/imageSlice.ts around lines 27–34, the legacy reducer
setImages only replaces images and preserves the existing source, which lets
stale source values persist and breaks selectNeedsGalleryRefresh; change
setImages to also set state.source to a clear non-gallery marker (e.g.,
state.source = 'non-gallery' or another agreed non-'gallery' string) so Home
will detect a non-gallery flow and trigger refresh, and as an alternative
follow-up migrate the five call sites to the explicit
setGalleryImages/setClusterImages/setSearchImages reducers and deprecate
setImages.

Comment on lines +29 to +35
const { data, isLoading, isSuccess, isError, error, refetch } = usePictoQuery(
{
queryKey: ['images'],
queryFn: () => fetchAllImages(),
enabled: !isSearchActive,
},
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find routing configuration to understand if Home stays mounted across navigation
find frontend/src -type f \( -name "*.tsx" -o -name "*.ts" \) | head -20
rg -n "Routes|createBrowserRouter|<Route" frontend/src --type=ts --type=tsx -A 3 | head -50

Repository: AOSSIE-Org/PictoPy

Length of output: 1004


🏁 Script executed:

#!/bin/bash
# Examine the Home.tsx file to see current implementation
wc -l frontend/src/pages/Home/Home.tsx
cat -n frontend/src/pages/Home/Home.tsx

Repository: AOSSIE-Org/PictoPy

Length of output: 3769


🏁 Script executed:

#!/bin/bash
# Find usePictoQuery hook definition
fd -e ts -e tsx | xargs rg -l "usePictoQuery" 
rg -n "export.*usePictoQuery|const usePictoQuery" frontend/src --type=ts --type=tsx -B 2 -A 15

Repository: AOSSIE-Org/PictoPy

Length of output: 919


🏁 Script executed:

#!/bin/bash
# Read the usePictoQuery hook implementation
cat -n frontend/src/hooks/useQueryExtension.ts

Repository: AOSSIE-Org/PictoPy

Length of output: 3519


🏁 Script executed:

#!/bin/bash
# Check App.tsx for routing setup
cat -n frontend/src/App.tsx

Repository: AOSSIE-Org/PictoPy

Length of output: 1528


🏁 Script executed:

#!/bin/bash
# Find and examine AppRoutes
find frontend/src -name "*AppRoutes*" -o -name "*routes*" | grep -E "\.(tsx|ts)$"

Repository: AOSSIE-Org/PictoPy

Length of output: 128


🏁 Script executed:

#!/bin/bash
# Read the routes file
fd -e ts -e tsx "routes" frontend/src | head -5

Repository: AOSSIE-Org/PictoPy

Length of output: 128


🏁 Script executed:

#!/bin/bash
# Read AppRoutes component
cat -n frontend/src/routes/AppRoutes.tsx

Repository: AOSSIE-Org/PictoPy

Length of output: 1500


🏁 Script executed:

#!/bin/bash
# Read routes constants
cat -n frontend/src/constants/routes.ts

Repository: AOSSIE-Org/PictoPy

Length of output: 401


🏁 Script executed:

#!/bin/bash
# Find where needsRefresh is set in redux
rg -n "setNeedsGalleryRefresh|selectNeedsGalleryRefresh" frontend/src -A 3 -B 1

Repository: AOSSIE-Org/PictoPy

Length of output: 5102


🏁 Script executed:

#!/antml
# Find the imageSlice to understand needsRefresh
fd -e ts -e tsx "imageSlice" frontend/src

Repository: AOSSIE-Org/PictoPy

Length of output: 148


🏁 Script executed:

#!/bin/bash
# Read imageSlice to understand setGalleryImages and source tracking
cat -n frontend/src/features/imageSlice.ts | head -100

Repository: AOSSIE-Org/PictoPy

Length of output: 2926


🏁 Script executed:

#!/antml
# Also check imageSelectors to see full implementation
cat -n frontend/src/features/imageSelectors.ts

Repository: AOSSIE-Org/PictoPy

Length of output: 1102


🏁 Script executed:

#!/antml
# Check if there's any documentation or comments about React Query behavior
rg -n "queryKey\|deduplic\|staleTime\|gcTime" frontend/src/hooks/useQueryExtension.ts frontend/src/config/ -A 2

Repository: AOSSIE-Org/PictoPy

Length of output: 44


🏁 Script executed:

#!/antml
# Look for QueryClient configuration
fd -e ts -e tsx "QueryClient" frontend/src/config/

Repository: AOSSIE-Org/PictoPy

Length of output: 105


🏁 Script executed:

#!/an/bash
# Read QueryClientProvider to understand React Query configuration
cat -n frontend/src/config/QueryClientProvider.tsx

Repository: AOSSIE-Org/PictoPy

Length of output: 529


Guard against redundant/double fetches: the "needsRefresh" effect can refetch on top of the initial query fetch.

When Home returns to the active route, usePictoQuery with enabled: !isSearchActive will fetch, and the useEffect can then trigger an additional refetch() while needsRefresh is still true. React Query's default deduplication window won't prevent both requests from executing.

A low-impact fix is to refetch only on a false -> true transition of needsRefresh (prevents redundant refetch when Home component remounts):

 export const Home = () => {
   const dispatch = useDispatch();
   const images = useSelector(selectImages);
   const needsRefresh = useSelector(selectNeedsGalleryRefresh);
+  const prevNeedsRefresh = useRef(needsRefresh);
   const scrollableRef = useRef<HTMLDivElement>(null);
   const [monthMarkers, setMonthMarkers] = useState<MonthMarker[]>([]);
   const searchState = useSelector((state: RootState) => state.search);
   const isSearchActive = searchState.active;

   const { data, isLoading, isSuccess, isError, error, refetch } = usePictoQuery(
     {
       queryKey: ['images'],
       queryFn: () => fetchAllImages(),
       enabled: !isSearchActive,
     },
   );

   // Refetch gallery images if returning from cluster/search view
   useEffect(() => {
-    if (!isSearchActive && needsRefresh) {
+    if (!isSearchActive && needsRefresh && !prevNeedsRefresh.current) {
       refetch();
     }
+    prevNeedsRefresh.current = needsRefresh;
   }, [needsRefresh, isSearchActive, refetch]);

Also applies to: 47-53

🤖 Prompt for AI Agents
In frontend/src/pages/Home/Home.tsx around lines 29-35 (and similarly at lines
47-53), the effect that calls refetch() can fire redundantly when the component
mounts because usePictoQuery already fetches; change the effect to only call
refetch when needsRefresh transitions from false to true by tracking the
previous needsRefresh value (e.g., use a ref or previous state) and performing
refetch only if prevNeedsRefresh === false and needsRefresh === true; update
both effect locations accordingly and clear/update the prev flag after refetch
so subsequent mounts won't trigger a duplicate fetch.

@Jitmisra
Copy link
Copy Markdown
Author

teamname bigbull

@rahulharpal1603
Copy link
Copy Markdown
Contributor

The issue's author wanted to work on it. So by the project rules, we assign the issue to them first.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-triage The maintainer needs time to review this issue. Please do not begin working on it.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants