Skip to content

Use a Managed Export CSV component that handles various states - #1222

Merged
khouadrired merged 8 commits into
mainfrom
refactor-exportcsv-button
Jul 15, 2026
Merged

Use a Managed Export CSV component that handles various states #1222
khouadrired merged 8 commits into
mainfrom
refactor-exportcsv-button

Conversation

@khouadrired

Copy link
Copy Markdown
Contributor

No description provided.

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a9d244cc-e99e-42a5-8f27-c8dbaa0cbe08

📥 Commits

Reviewing files that changed from the base of the PR and between 0a2d0ba and b487a09.

📒 Files selected for processing (1)
  • src/components/ui/csvDownloader/managed-export-csv-button.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/ui/csvDownloader/managed-export-csv-button.tsx

📝 Walkthrough

Walkthrough

This PR adds ManagedExportCsvButton, updates CsvExport to use an async exportCsv callback with resetKey, and extends the CSV downloader exports and prop types accordingly.

Changes

Managed CSV export flow

Layer / File(s) Summary
ManagedExportCsvButton component implementation
src/components/ui/csvDownloader/managed-export-csv-button.tsx
Defines the managed export props, tracks loading and success state, resets on resetKey changes, clears success when disabled, and wraps exportCsv with success and error callbacks before rendering ExportCsvButton.
CsvExport integration and exports
src/components/ui/csvDownloader/csv-export.tsx, src/components/ui/csvDownloader/csv-export.type.ts, src/components/ui/csvDownloader/index.ts
CsvExport now uses ManagedExportCsvButton, passes an async exportCsv callback from csvExport.getData(...), accepts resetKey, and the module re-exports the new button alongside the updated prop type.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant ManagedExportCsvButton
  participant ExportCsvButton
  participant CsvExport
  participant CsvExportService

  User->>ExportCsvButton: click
  ExportCsvButton->>ManagedExportCsvButton: handleClick
  ManagedExportCsvButton->>ManagedExportCsvButton: set isLoading=true
  ManagedExportCsvButton->>CsvExport: exportCsv()
  CsvExport->>CsvExportService: getData(...)
  CsvExportService-->>CsvExport: result/error
  CsvExport-->>ManagedExportCsvButton: resolve/reject
  alt success
    ManagedExportCsvButton->>ManagedExportCsvButton: set isSuccessful=true
    ManagedExportCsvButton->>ManagedExportCsvButton: onSuccess()
  else error
    ManagedExportCsvButton->>ManagedExportCsvButton: onError()
  end
  ManagedExportCsvButton->>ExportCsvButton: re-render with updated state
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive No meaningful pull request description was provided, so its relevance cannot be assessed. Add a short description summarizing the CSV export refactor and the new managed component behavior.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: introducing a managed CSV export component with state handling.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/components/ui/csvDownloader/csv-export.tsx (1)

23-23: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Widen return type from JSX.Element to React.ReactNode.

Static analysis flags this as too narrow: JSX.Element excludes null, strings, numbers, and fragments that components may return in the future.

🔧 Proposed fix
-}: CsvExportProps): JSX.Element {
+}: CsvExportProps): React.ReactNode {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/ui/csvDownloader/csv-export.tsx` at line 23, The CsvExport
component’s return type is too narrow in its function signature, so update the
`CsvExport` declaration to return `React.ReactNode` instead of `JSX.Element`.
Keep the implementation aligned with the component’s actual output so it can
safely return fragments, null, or other valid React nodes in the future.

Source: Linters/SAST tools

src/components/ui/csvDownloader/managed-export-csv-button.tsx (1)

26-38: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Two booleans could drift into an inconsistent combination.

isLoading and isSuccessful are independent state variables; nothing enforces mutual exclusivity beyond the ordering in handleClick. Consider consolidating into a single status enum ('idle' | 'loading' | 'success' | 'error') to make invalid combinations unrepresentable and simplify the two useEffect resets into one.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/ui/csvDownloader/managed-export-csv-button.tsx` around lines
26 - 38, The state in managed-export-csv-button’s component can drift because
isLoading and isSuccessful are managed independently. Refactor the related logic
in the component (including handleClick and the useEffect resets) to use a
single status value such as 'idle' | 'loading' | 'success' | 'error' so invalid
combinations cannot occur. Update the reset behavior for resetKey and disabled
to set that one status consistently instead of toggling two separate booleans.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/ui/csvDownloader/managed-export-csv-button.tsx`:
- Around line 40-54: handleClick in ManagedExportCsvButton can update state and
invoke callbacks after the component unmounts while exportCsv is still pending.
Add a mounted ref (set in the component lifecycle) and check it before calling
setIsSuccessful, setIsLoading, onSuccess, or onError, so only mounted instances
are updated. Keep the async flow in handleClick the same, but guard every
post-await branch and the finally cleanup with the mounted check.

---

Nitpick comments:
In `@src/components/ui/csvDownloader/csv-export.tsx`:
- Line 23: The CsvExport component’s return type is too narrow in its function
signature, so update the `CsvExport` declaration to return `React.ReactNode`
instead of `JSX.Element`. Keep the implementation aligned with the component’s
actual output so it can safely return fragments, null, or other valid React
nodes in the future.

In `@src/components/ui/csvDownloader/managed-export-csv-button.tsx`:
- Around line 26-38: The state in managed-export-csv-button’s component can
drift because isLoading and isSuccessful are managed independently. Refactor the
related logic in the component (including handleClick and the useEffect resets)
to use a single status value such as 'idle' | 'loading' | 'success' | 'error' so
invalid combinations cannot occur. Update the reset behavior for resetKey and
disabled to set that one status consistently instead of toggling two separate
booleans.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: eda20c91-2315-473b-8585-e6bc9d9f41e5

📥 Commits

Reviewing files that changed from the base of the PR and between 63d9306 and 0a2d0ba.

📒 Files selected for processing (4)
  • src/components/ui/csvDownloader/csv-export.tsx
  • src/components/ui/csvDownloader/csv-export.type.ts
  • src/components/ui/csvDownloader/index.ts
  • src/components/ui/csvDownloader/managed-export-csv-button.tsx

Comment on lines +40 to +54
const handleClick = useCallback(async () => {
setIsSuccessful(false);
setIsLoading(true);

try {
await exportCsv();
setIsSuccessful(true);
onSuccess?.();
} catch (error) {
setIsSuccessful(false);
onError?.(error);
} finally {
setIsLoading(false);
}
}, [exportCsv, onSuccess, onError]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Guard against state updates after unmount.

handleClick awaits exportCsv() and then calls setIsSuccessful/setIsLoading/onSuccess/onError regardless of whether the component is still mounted. If the consumer unmounts while the export is in flight (e.g., navigating away mid-download), these calls will fire on an unmounted component, triggering React state-update warnings and potentially masking a stale closure over onSuccess/onError.

🔧 Proposed fix using a mounted ref
+    const isMountedRef = useRef(true);
+    useEffect(() => {
+        return () => {
+            isMountedRef.current = false;
+        };
+    }, []);
+
     const handleClick = useCallback(async () => {
         setIsSuccessful(false);
         setIsLoading(true);

         try {
             await exportCsv();
-            setIsSuccessful(true);
-            onSuccess?.();
+            if (isMountedRef.current) {
+                setIsSuccessful(true);
+                onSuccess?.();
+            }
         } catch (error) {
-            setIsSuccessful(false);
-            onError?.(error);
+            if (isMountedRef.current) {
+                setIsSuccessful(false);
+                onError?.(error);
+            }
         } finally {
-            setIsLoading(false);
+            if (isMountedRef.current) {
+                setIsLoading(false);
+            }
         }
     }, [exportCsv, onSuccess, onError]);
📝 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
const handleClick = useCallback(async () => {
setIsSuccessful(false);
setIsLoading(true);
try {
await exportCsv();
setIsSuccessful(true);
onSuccess?.();
} catch (error) {
setIsSuccessful(false);
onError?.(error);
} finally {
setIsLoading(false);
}
}, [exportCsv, onSuccess, onError]);
const isMountedRef = useRef(true);
useEffect(() => {
return () => {
isMountedRef.current = false;
};
}, []);
const handleClick = useCallback(async () => {
setIsSuccessful(false);
setIsLoading(true);
try {
await exportCsv();
if (isMountedRef.current) {
setIsSuccessful(true);
onSuccess?.();
}
} catch (error) {
if (isMountedRef.current) {
setIsSuccessful(false);
onError?.(error);
}
} finally {
if (isMountedRef.current) {
setIsLoading(false);
}
}
}, [exportCsv, onSuccess, onError]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/ui/csvDownloader/managed-export-csv-button.tsx` around lines
40 - 54, handleClick in ManagedExportCsvButton can update state and invoke
callbacks after the component unmounts while exportCsv is still pending. Add a
mounted ref (set in the component lifecycle) and check it before calling
setIsSuccessful, setIsLoading, onSuccess, or onError, so only mounted instances
are updated. Keep the async flow in handleClick the same, but guard every
post-await branch and the finally cleanup with the mounted check.

@sonarqubecloud

Copy link
Copy Markdown

@khouadrired
khouadrired merged commit b912e2d into main Jul 15, 2026
5 checks passed
@khouadrired
khouadrired deleted the refactor-exportcsv-button branch July 15, 2026 12:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants