From 648fd4856bb18e573beb16a136d9b090b7787f8d Mon Sep 17 00:00:00 2001 From: ilhom Date: Wed, 22 Jul 2026 13:39:55 +0700 Subject: [PATCH 1/2] fix(rm-costs): fix period filter not working after page reload with URL params - Fix stale closure in auto-select effect reading filters.period from a potentially outdated closure instead of the latest value. Added filtersPeriodRef to always read the most up-to-date period value, preventing the auto-select from overriding URL-provided period. - Fix useUrlState stateRef sync from useEffect to synchronous render-time assignment, ensuring setState updater always reads the latest state even when called from other effects in the same render cycle. --- .../rm-pricing/costs/rm-costs-page-client.tsx | 12 ++++++++++-- src/lib/hooks/use-url-state.ts | 12 +++++------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/app/(dashboard)/finance/rm-pricing/costs/rm-costs-page-client.tsx b/src/app/(dashboard)/finance/rm-pricing/costs/rm-costs-page-client.tsx index 85fd482..80c001a 100644 --- a/src/app/(dashboard)/finance/rm-pricing/costs/rm-costs-page-client.tsx +++ b/src/app/(dashboard)/finance/rm-pricing/costs/rm-costs-page-client.tsx @@ -55,9 +55,15 @@ function RMCostsPageContent() { // Track whether the initial auto-select has fired (declared before listLoading uses it). const autoSelectedRef = useRef(false) + // Keep a ref to the latest filters.period so the auto-select effect always + // reads the most up-to-date value (avoids stale-closure issues). + const filtersPeriodRef = useRef(filters.period) + filtersPeriodRef.current = filters.period + // Once autoSelectedRef is true the user has either received a default period // or deliberately picked "All Periods" (period=""), so we no longer block on - // !filters.period. + // !filters.period. Also skip the "waiting" gate when a period is already + // present from the URL (e.g. after a page reload with ?period=202604). const listLoading = periodsLoading || (!autoSelectedRef.current && availablePeriods.length > 0 && !filters.period) || isLoading // Auto-select the latest available period ONCE on first load when the user @@ -68,7 +74,9 @@ function RMCostsPageContent() { if (autoSelectedRef.current) return if (availablePeriods.length === 0) return autoSelectedRef.current = true - if (!filters.period) { + // Read period from ref to guarantee we see the latest value (the URL may + // already carry a period param after a page reload). + if (!filtersPeriodRef.current) { setFilters((prev) => ({ ...prev, period: availablePeriods[0], page: 1 })) } // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/src/lib/hooks/use-url-state.ts b/src/lib/hooks/use-url-state.ts index f11e733..d6f7925 100644 --- a/src/lib/hooks/use-url-state.ts +++ b/src/lib/hooks/use-url-state.ts @@ -1,7 +1,7 @@ "use client" import { useSearchParams, useRouter, usePathname } from "next/navigation" -import { useCallback, useMemo, useRef, useEffect } from "react" +import { useCallback, useMemo, useRef } from "react" /** * Configuration for URL state serialization @@ -121,13 +121,11 @@ export function useUrlState( }, [searchParams, defaultValues, deserialize]) // Use ref to access current state in callback without adding it to dependencies - // This prevents setState from changing reference on every state change + // This prevents setState from changing reference on every state change. + // Sync the ref during render (not in an effect) so that setState always reads + // the latest state, even when called from other effects in the same cycle. const stateRef = useRef(state) - - // Sync ref in effect to satisfy ESLint rules while keeping the pattern - useEffect(() => { - stateRef.current = state - }, [state]) + stateRef.current = state // Update URL with new state - NO startTransition for immediate updates const setState = useCallback( From 6adb9d10e3320423698d2a12c88272587e43101f Mon Sep 17 00:00:00 2001 From: ilhom Date: Wed, 22 Jul 2026 14:31:17 +0700 Subject: [PATCH 2/2] fix(hooks): avoid writing ref during render in useUrlState React Compiler's react-hooks/refs rule flags mutating stateRef.current during render; move the assignment into a useEffect so it runs after commit instead, fixing the frontend lint CI failure. --- src/lib/hooks/use-url-state.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/lib/hooks/use-url-state.ts b/src/lib/hooks/use-url-state.ts index d6f7925..17c993d 100644 --- a/src/lib/hooks/use-url-state.ts +++ b/src/lib/hooks/use-url-state.ts @@ -1,7 +1,7 @@ "use client" import { useSearchParams, useRouter, usePathname } from "next/navigation" -import { useCallback, useMemo, useRef } from "react" +import { useCallback, useEffect, useMemo, useRef } from "react" /** * Configuration for URL state serialization @@ -125,7 +125,9 @@ export function useUrlState( // Sync the ref during render (not in an effect) so that setState always reads // the latest state, even when called from other effects in the same cycle. const stateRef = useRef(state) - stateRef.current = state + useEffect(() => { + stateRef.current = state + }, [state]) // Update URL with new state - NO startTransition for immediate updates const setState = useCallback(