Skip to content

Store the search bar and field toggle state in sessionStorage#430

Merged
SvenVw merged 15 commits into
developmentfrom
FDM314
Jan 27, 2026
Merged

Store the search bar and field toggle state in sessionStorage#430
SvenVw merged 15 commits into
developmentfrom
FDM314

Conversation

@BoraIneviNMI
Copy link
Copy Markdown
Collaborator

@BoraIneviNMI BoraIneviNMI commented Jan 23, 2026

Enhancements

  • Improve the user experience when they come back to the field and rotation tables, by storing their filters in the session storage.

Summary by CodeRabbit

  • New Features

    • User search filters and field selections are now persisted in session storage, retaining your preferences as you navigate
  • UI/UX Improvements

    • Improved table layout with better responsive behavior and content overflow handling
    • Enhanced visual consistency across the interface

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

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Jan 23, 2026

🦋 Changeset detected

Latest commit: d305d6a

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@svenvw/fdm-app Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 23, 2026

Walkthrough

The PR refactors state management for field filtering and row selection by introducing Zustand stores, moving from local component state to persistent store-based state. The storage layer is enhanced to support both localStorage and sessionStorage, with corresponding updates to table components and UI layout adjustments.

Changes

Cohort / File(s) Summary
Store Infrastructure
fdm-app/app/store/field-filter.ts, fdm-app/app/store/field-selection.ts, fdm-app/app/store/storage.ts
Enhanced storage layer to support both localStorage and sessionStorage. Introduced useFieldFilterStore (searchTerms, farmId, setSearchTerms, syncFarm) and useFieldSelectionStore (fieldIds, setFieldIds, syncFarm) with persistent session storage for maintaining filter and selection state across navigation.
Table Component Refactoring
fdm-app/app/components/blocks/fields/table.tsx, fdm-app/app/components/blocks/rotation/table.tsx
Migrated from local state to store-driven filtering and row selection. Replaced local globalFilter and rowSelection state with fieldFilter and fieldSelectionStore bindings. Integrated farm synchronization and updated row selection logic to derive state from fieldIds.
Selection Logic Simplification
fdm-app/app/components/blocks/rotation/columns.tsx
Removed parent-child row selection propagation logic from checkbox handler, simplifying hierarchical selection behavior.
Layout & UI Adjustments
fdm-app/app/components/blocks/farm/farm-content.tsx, fdm-app/app/routes/farm.$b_id_farm.$calendar.rotation._index.tsx
Added min-width constraints to flex containers to prevent improper shrinking. Simplified JSX structure by removing intermediate wrapper div.
Documentation
.changeset/tired-areas-take.md
Added changeset entry documenting minor version bump with improved UX for session-persisted user filters.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

bug

Suggested reviewers

  • gerardhros

Poem

🐰 A rabbit's tale of stores so grand,
Where filters dance and fieldIds stand,
From local states to sessions bright,
The selections persist through the night!
Zustand brings order, no more state fright!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main objective of the pull request: persisting search bar and field toggle state in sessionStorage across the field and rotation tables.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch FDM314

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.

@codecov
Copy link
Copy Markdown

codecov Bot commented Jan 23, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 88.07%. Comparing base (5f3e6a9) to head (d305d6a).
⚠️ Report is 16 commits behind head on development.

Additional details and impacted files
@@             Coverage Diff              @@
##           development     #430   +/-   ##
============================================
  Coverage        88.07%   88.07%           
============================================
  Files               91       91           
  Lines             4621     4621           
  Branches          1492     1492           
============================================
  Hits              4070     4070           
  Misses             551      551           
Flag Coverage Δ
fdm-calculator 88.76% <ø> (ø)
fdm-core 86.77% <ø> (ø)
fdm-data 92.12% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@coderabbitai coderabbitai Bot added branch:development Issue only affecting development, not the main branch (yet) enhancement New feature or request fdm-app labels Jan 23, 2026
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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
fdm-app/app/components/blocks/fields/table.tsx (1)

127-133: Update fuzzyFilter to handle both searchTerms and showProductiveOnly.

The FieldFilterToggle component is rendered in this table and updates showProductiveOnly in the store, but the fuzzyFilter function only destructures searchTerms and ignores the showProductiveOnly parameter. The toggle has no effect on table filtering. Compare with the rotation table's fuzzySearchAndProductivityFilter (lines 211-231), which implements filtering logic for both parameters. Apply similar filtering to maintain consistency and ensure the toggle works as expected.

🤖 Fix all issues with AI agents
In `@fdm-app/app/components/blocks/fields/table.tsx`:
- Line 144: The onGlobalFilterChange handler currently calls
useFieldFilterStore.setState(state) which can overwrite unrelated store keys;
change it to extract/validate the expected filter value (e.g. ensure the
incoming state has the expected globalFilter shape or value) and then update the
store using a partial/merge update (call useFieldFilterStore.setState(prev => ({
...prev, globalFilter: validatedValue }))). Update the handler referenced as
onGlobalFilterChange to perform the validation/extraction and only set the
specific store key instead of replacing the whole state.

In `@fdm-app/app/components/blocks/rotation/table.tsx`:
- Around line 246-249: The condition in the onGlobalFilterChange handler uses
incorrect operator precedence so the comparison against fieldFilter.searchTerms
is not performed; change the expression to first resolve the possibly-null
searchTerms (e.g., compute const newTerms = globalFilter?.searchTerms ?? "" or
wrap the coalescing in parentheses) and then compare newTerms !==
fieldFilter.searchTerms, then call fieldFilter.setSearchTerms(newTerms) when
they differ; update the onGlobalFilterChange block (referencing
onGlobalFilterChange, globalFilter?.searchTerms, fieldFilter.searchTerms, and
fieldFilter.setSearchTerms) accordingly.
🧹 Nitpick comments (1)
fdm-app/app/store/storage.ts (1)

3-5: Consider narrowing the type for better type safety.

The parameter type keyof Window is overly broad and doesn't constrain to storage-compatible properties. This allows passing invalid keys like "document" or "navigator" that would fail at runtime.

✨ Suggested improvement
-const createSSRStorage = (name: keyof Window): StateStorage => {
+const createSSRStorage = (name: "localStorage" | "sessionStorage"): StateStorage => {
     if (typeof window !== "undefined") {
-        return window[name]
+        return window[name] as StateStorage
     }

Comment thread fdm-app/app/components/blocks/fields/table.tsx Outdated
@BoraIneviNMI BoraIneviNMI linked an issue Jan 26, 2026 that may be closed by this pull request
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: 2

🤖 Fix all issues with AI agents
In `@fdm-app/app/store/rotation-selection.tsx`:
- Around line 28-30: Remove the debug logging in the store: delete or guard the
console.log calls inside setSelectionFrom (and the other console.log at the
later occurrence) so internal row data is not leaked to production; either
remove the console.log(rows, selection) entirely from setSelectionFrom or wrap
it behind a development-only check (e.g., process.env.NODE_ENV ===
"development") so logs only occur in dev environments.
- Around line 69-71: The persistence key for the rotation-selection store is
reusing "field-filter-storage" and will clash with the field-filter store;
update the store's config to use a unique name like "rotation-selection-storage"
(change the name property in the rotation-selection store definition where
storage: createJSONStorage(() => ssrSafeSessionJSONStorage) is used) so the
rotation-selection state ({ selection: Record<string, Record<string, boolean>>
}) won’t be mixed with the field-filter state.
🧹 Nitpick comments (1)
fdm-app/app/store/rotation-selection.tsx (1)

35-40: Coerce selection values to booleans for a stable persisted shape.

selection[fieldRow.id] can be undefined, which yields non-boolean values in selection and inconsistent JSON output. Coercing to !! keeps the store shape stable and matches the type.

♻️ Suggested tweak
-                    subSelection[(fieldRow.original as FieldRow).b_id] =
-                        selection[fieldRow.id]
+                    subSelection[(fieldRow.original as FieldRow).b_id] =
+                        !!selection[fieldRow.id]

Comment thread fdm-app/app/store/rotation-selection.tsx Outdated
Comment thread fdm-app/app/store/rotation-selection.ts Outdated
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: 1

🤖 Fix all issues with AI agents
In `@fdm-app/app/components/blocks/rotation/table.tsx`:
- Around line 280-290: The first effect currently depends on the stable
fieldFilter reference so it won’t re-run when its internals change; change the
dependency to the specific properties (e.g., fieldFilter.searchTerms and
fieldFilter.showProductiveOnly) and keep the body calling
rotationSelectionStore.clear(); the second effect should depend on actual row
data and the store selection rather than method/store references—inside the
effect call const rows = table.getCoreRowModel().rows and then
setRowSelection(rotationSelectionStore.getSelectionFor(rows)), and use a
dependency array like [rows.map(r => r.id).join(','),
rotationSelectionStore.getState().selection] or subscribe to
rotationSelectionStore and use its selection value to trigger the effect so
rowSelection stays in sync (referencing table.getCoreRowModel,
rotationSelectionStore.getSelectionFor, setRowSelection).
♻️ Duplicate comments (1)
fdm-app/app/store/rotation-selection.ts (1)

61-64: Use a unique persist key for the rotation selection store.

This store uses "field-filter-storage", which is already used by the field-filter store (fdm-app/app/store/field-filter.ts). Since they have incompatible state shapes—field-filter stores { showProductiveOnly: boolean; searchTerms: string } while rotation-selection stores { selection: Record<string, Record<string, boolean>> }—they will corrupt each other's data during sessionStorage hydration.

Suggested fix
         {
-            name: "field-filter-storage", // unique name
+            name: "rotation-selection-storage",
             storage: createJSONStorage(() => ssrSafeSessionJSONStorage), // Use SSR-safe storage
         },
🧹 Nitpick comments (1)
fdm-app/app/store/rotation-selection.ts (1)

11-19: Consider renaming the interface to match its purpose.

The interface is named FieldFilterState but this store manages rotation row selections, not field filtering. This naming inconsistency could cause confusion, especially since there's already a FieldFilterState concept in field-filter.ts.

Suggested rename
-interface FieldFilterState {
+interface RotationSelectionState {
     selection: Record<string, Record<string, boolean>>
     clear(): void
     getSelectionFor(rows: Row<RotationExtended>[]): Record<string, boolean>
     setSelectionFrom(
         rows: Row<RotationExtended>[],
         rowSelection: Record<string, boolean>,
     ): void
 }
 
-export const useRotationSelectionStore = create<FieldFilterState>()(
+export const useRotationSelectionStore = create<RotationSelectionState>()(

Comment thread fdm-app/app/components/blocks/rotation/table.tsx Outdated
@BoraIneviNMI BoraIneviNMI requested a review from SvenVw January 26, 2026 13:17
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: 2

🤖 Fix all issues with AI agents
In `@fdm-app/app/components/blocks/fields/table.tsx`:
- Around line 172-183: The effect that sets rowSelection (using setRowSelection
and table.getCoreRowModel) currently only depends on fieldSelection and can
become stale when table data changes; update the dependency list to include a
stable representation of the table rows or the data (e.g., include
table.getCoreRowModel().rows or the memoized data used to create the table) so
the effect re-runs when rows change, and ensure you derive the selection from
table.getCoreRowModel().rows and fieldSelection.fieldIds inside the effect to
recompute mapping for new/removed rows.
- Around line 147-150: The onGlobalFilterChange handler currently treats the
incoming globalFilter as a direct value and ignores the Updater<T> function
form; update the handler (onGlobalFilterChange) to accept either a value or
updater function like handleRowSelection does: detect if globalFilter is a
function and call it with the previous fieldFilter.searchTerms to derive the new
value, otherwise use globalFilter directly, then call fieldFilter.setSearchTerms
with that resolved string (falling back to "" if undefined).
🧹 Nitpick comments (1)
fdm-app/app/store/field-selection.ts (1)

5-8: Rename the state type to match field selection.

FieldFilterState is easy to confuse with the field-filter store. Consider FieldSelectionState for clarity and consistency.

♻️ Suggested rename
-interface FieldFilterState {
+interface FieldSelectionState {
     fieldIds: string[]
     setFieldIds: (fieldIds: string[]) => void
 }
 
-export const useFieldSelectionStore = create<FieldFilterState>()(
+export const useFieldSelectionStore = create<FieldSelectionState>()(

Comment thread fdm-app/app/components/blocks/fields/table.tsx Outdated
Comment thread fdm-app/app/components/blocks/fields/table.tsx Outdated
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: 1

🤖 Fix all issues with AI agents
In `@fdm-app/app/components/blocks/rotation/table.tsx`:
- Around line 244-247: The onGlobalFilterChange handler incorrectly assumes its
argument is a value; update it to handle an updater-or-value by detecting if
globalFilter is a function and, if so, calling it with the current filter state
(fieldFilter.searchTerms) to compute the new search terms before calling
fieldFilter.setSearchTerms; mirror the pattern used in
./fdm-app/app/components/blocks/fields/table.tsx so both direct values and
updater functions are supported.

Comment thread fdm-app/app/components/blocks/rotation/table.tsx Outdated
@BoraIneviNMI
Copy link
Copy Markdown
Collaborator Author

Some users have an issue on the rotation table where they are not able to select the crop rows using the checkbox. Might also be clicking the row I am not very sure, but the former is more likely.

@BoraIneviNMI
Copy link
Copy Markdown
Collaborator Author

@coderabbitai Some users have an issue on the rotation table where they are not able to select the crop rows using the checkbox. Might also be clicking the row I am not very sure, but the former is more likely.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 27, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Read the rotation table component to understand the selection logic
cat fdm-app/app/components/blocks/rotation/table.tsx

Length of output: 25371


🏁 Script executed:

#!/bin/bash
# Read the rotation columns to see the checkbox implementation
cat fdm-app/app/components/blocks/rotation/columns.tsx

Length of output: 15574


🏁 Script executed:

#!/bin/bash
# Read the rotation-selection store implementation
cat fdm-app/app/store/rotation-selection.ts

Length of output: 2608


@BoraIneviNMI I found the issue! The problem is in the handleRowSelection function in fdm-app/app/components/blocks/rotation/table.tsx.

The Bug

When a user clicks a checkbox, the selection flow is:

  1. row.toggleSelected() is called
  2. TanStack Table calls onRowSelectionChange (which routes through handleRowSelection)
  3. handleRowSelection updates the store via setSelectionFrom
  4. BUT it doesn't update the local rowSelection state
  5. Since the table uses controlled state, it expects onRowSelectionChange to update the local state
  6. The checkbox doesn't appear checked because rowSelection remains stale

The current code (Line 80-84):

function handleRowSelection(selection: Record<string, boolean>) {
    rotationSelectionStore.setSelectionFrom(
        table.getCoreRowModel().rows,
        selection,
    )
}

The Fix

The function needs to update the local state immediately:

function handleRowSelection(selection: Record<string, boolean>) {
    setRowSelection(selection)  // Add this line to update local state
    rotationSelectionStore.setSelectionFrom(
        table.getCoreRowModel().rows,
        selection,
    )
}

This ensures that:

  1. The local rowSelection state is updated immediately when checkboxes are clicked
  2. The store is also updated for persistence
  3. The checkbox visual state reflects the selection correctly

The useEffect that syncs from the store back to local state will still work for restoring selections when the page is loaded or data changes.


🧠 Learnings used
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 272
File: fdm-app/app/store/field-fertilizer-form.tsx:45-47
Timestamp: 2025-09-25T15:10:59.708Z
Learning: In the FDM application, Zustand stores with persist middleware using sessionStorage/localStorage don't require SSR hardening guards. The existing store patterns in fdm-app work without typeof window checks or memory storage fallbacks.

Learnt from: SvenVw
Repo: SvenVw/fdm PR: 272
File: fdm-app/app/store/field-fertilizer-form.tsx:45-47
Timestamp: 2025-09-25T15:10:59.708Z
Learning: In the FDM application, Zustand stores with persist middleware using sessionStorage/localStorage don't require SSR hardening guards. The existing store patterns in fdm-app work without typeof window checks or memory storage fallbacks, as evidenced by the changelog store using createJSONStorage(() => localStorage) directly.

Learnt from: SvenVw
Repo: SvenVw/fdm PR: 376
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.elevation.tsx:187-213
Timestamp: 2025-12-15T12:19:58.494Z
Learning: In the FDM farm management application, storing map viewState (including longitude and latitude coordinates) in sessionStorage is an acceptable design decision. The coordinates primarily represent farm field locations (business data) rather than sensitive personal addresses. Users explicitly grant geolocation permission when enabling the tracking feature via GeolocateControl, and sessionStorage is client-side only. This serves essential functionality for map state restoration across sessions.

@BoraIneviNMI
Copy link
Copy Markdown
Collaborator Author

BoraIneviNMI commented Jan 27, 2026

@coderabbitai why doesn't the useeffect help here? The rotationselectionstate changes which is synched through userotationselectionstore. Am I wrong possibly?

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 27, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Read the rotation table to examine the useEffect and store subscription
cat fdm-app/app/components/blocks/rotation/table.tsx | grep -A 20 "useEffect"

Length of output: 2129


🏁 Script executed:

#!/bin/bash
# Check how rotationSelectionStore is used and subscribed to
cat fdm-app/app/components/blocks/rotation/table.tsx | grep -B 5 -A 5 "rotationSelectionStore"

Length of output: 1027


🏁 Script executed:

#!/bin/bash
# Get the full context around rowSelection state and its initialization
cat fdm-app/app/components/blocks/rotation/table.tsx | sed -n '60,120p'

Length of output: 2083


🏁 Script executed:

#!/bin/bash
# Check the store implementation to see what the hook returns
cat fdm-app/app/store/rotation-selection.ts

Length of output: 2608


@BoraIneviNMI Excellent question! You're absolutely right to question this. Let me explain the timing issue:

Why the useEffect Doesn't Help

When you click a checkbox, here's what happens:

  1. handleRowSelection calls setSelectionFrom → updates store's internal selection state
  2. ✅ The component re-renders (because useRotationSelectionStore() subscribes to all changes)
  3. But the useEffect doesn't run!

The Problem

Look at the useEffect dependencies:

useEffect(() => {
    setRowSelection(
        rotationSelectionStore.getSelectionFor(table.getCoreRowModel().rows),
    )
}, [memoizedData, table.getCoreRowModel, rotationSelectionStore])

The issue is that rotationSelectionStore is the store object itself, which is a stable reference that never changes. Even though the state inside the store changes, the store object reference stays the same.

So during the re-render triggered by the store update:

  • memoizedData → unchanged ❌
  • table.getCoreRowModel → unchanged ❌
  • rotationSelectionStore → unchanged ❌ (stable object reference)

None of the dependencies changed, so the useEffect is skipped!

The Solution

You need to update the local state immediately in handleRowSelection:

function handleRowSelection(selection: Record<string, boolean>) {
    setRowSelection(selection)  // ← Update local state immediately
    rotationSelectionStore.setSelectionFrom(
        table.getCoreRowModel().rows,
        selection,
    )
}

This way:

  • The checkbox responds immediately (local state updated)
  • The store is also updated for persistence
  • The useEffect still works for initial page load and data changes

Great catch on questioning this! 🎯


🧠 Learnings used
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 272
File: fdm-app/app/store/field-fertilizer-form.tsx:45-47
Timestamp: 2025-09-25T15:10:59.708Z
Learning: In the FDM application, Zustand stores with persist middleware using sessionStorage/localStorage don't require SSR hardening guards. The existing store patterns in fdm-app work without typeof window checks or memory storage fallbacks.

Learnt from: SvenVw
Repo: SvenVw/fdm PR: 272
File: fdm-app/app/store/field-fertilizer-form.tsx:45-47
Timestamp: 2025-09-25T15:10:59.708Z
Learning: In the FDM application, Zustand stores with persist middleware using sessionStorage/localStorage don't require SSR hardening guards. The existing store patterns in fdm-app work without typeof window checks or memory storage fallbacks, as evidenced by the changelog store using createJSONStorage(() => localStorage) directly.

Learnt from: SvenVw
Repo: SvenVw/fdm PR: 376
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.elevation.tsx:187-213
Timestamp: 2025-12-15T12:19:58.494Z
Learning: In the FDM farm management application, storing map viewState (including longitude and latitude coordinates) in sessionStorage is an acceptable design decision. The coordinates primarily represent farm field locations (business data) rather than sensitive personal addresses. Users explicitly grant geolocation permission when enabling the tracking feature via GeolocateControl, and sessionStorage is client-side only. This serves essential functionality for map state restoration across sessions.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 27, 2026

Tip

For best results, initiate chat on the files or code changes.

@BoraIneviNMI Great question - let me clarify the distinction between Zustand's subscription and useEffect dependencies.

Zustand Subscription vs useEffect Dependencies

You're correct that when setSelectionFrom is called, the store's internal state changes. This does trigger a component re-render through Zustand's subscription mechanism. However, the re-render alone doesn't cause the useEffect to run.

The useEffect only runs when one of its dependencies changes:

useEffect(() => {
    setRowSelection(
        rotationSelectionStore.getSelectionFor(table.getCoreRowModel().rows),
    )
}, [memoizedData, table.getCoreRowModel, rotationSelectionStore])

The issue: rotationSelectionStore is the store object itself - a stable reference that never changes. What changes is rotationSelectionStore.selection (the state inside), but that's not in the dependency array.

Two Possible Solutions

Option 1 (Recommended): Update local state immediately in handleRowSelection:

function handleRowSelection(selection: Record<string, boolean>) {
    setRowSelection(selection)  // Immediate update
    rotationSelectionStore.setSelectionFrom(table.getCoreRowModel().rows, selection)
}

Option 2: Fix the useEffect dependency to include the actual state:

}, [memoizedData, table.getCoreRowModel, rotationSelectionStore.selection])

Option 1 is cleaner because it updates immediately without waiting for the next render cycle. Does this clarify the distinction?


🧠 Learnings used
Learnt from: SvenVw
Repo: SvenVw/fdm PR: 272
File: fdm-app/app/store/field-fertilizer-form.tsx:45-47
Timestamp: 2025-09-25T15:10:59.708Z
Learning: In the FDM application, Zustand stores with persist middleware using sessionStorage/localStorage don't require SSR hardening guards. The existing store patterns in fdm-app work without typeof window checks or memory storage fallbacks.

Learnt from: SvenVw
Repo: SvenVw/fdm PR: 272
File: fdm-app/app/store/field-fertilizer-form.tsx:45-47
Timestamp: 2025-09-25T15:10:59.708Z
Learning: In the FDM application, Zustand stores with persist middleware using sessionStorage/localStorage don't require SSR hardening guards. The existing store patterns in fdm-app work without typeof window checks or memory storage fallbacks, as evidenced by the changelog store using createJSONStorage(() => localStorage) directly.

Learnt from: SvenVw
Repo: SvenVw/fdm PR: 376
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.elevation.tsx:187-213
Timestamp: 2025-12-15T12:19:58.494Z
Learning: In the FDM farm management application, storing map viewState (including longitude and latitude coordinates) in sessionStorage is an acceptable design decision. The coordinates primarily represent farm field locations (business data) rather than sensitive personal addresses. Users explicitly grant geolocation permission when enabling the tracking feature via GeolocateControl, and sessionStorage is client-side only. This serves essential functionality for map state restoration across sessions.

…e using a single useFieldSelectionStore, implemented syncFarm to clear selection on farm switch, and removed legacy rotation-selection store.
@coderabbitai coderabbitai Bot added the bug Something isn't working label Jan 27, 2026
@SvenVw SvenVw merged commit 3487a78 into development Jan 27, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

branch:development Issue only affecting development, not the main branch (yet) bug Something isn't working enhancement New feature or request fdm-app

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Store the search bar and field toggle state in sessionStorage

2 participants