Skip to content

Comments

fix: use functional setState to avoid stale closures in show-billing#3778

Open
karyanayandi wants to merge 1 commit intoDokploy:canaryfrom
karyanayandi:fix/functional-setstate-stale-closures
Open

fix: use functional setState to avoid stale closures in show-billing#3778
karyanayandi wants to merge 1 commit intoDokploy:canaryfrom
karyanayandi:fix/functional-setstate-stale-closures

Conversation

@karyanayandi
Copy link

@karyanayandi karyanayandi commented Feb 22, 2026

What is this PR about?

Direct reads of serverQuantity inside onClick handlers can produce stale values in closures. Use the callback form setState(prev => ...) so React always supplies the latest state value.

Checklist

Before submitting this PR, please make sure that:

  • You created a dedicated branch based on the canary branch.
  • You have read the suggestions in the CONTRIBUTING.md file https://github.com/Dokploy/dokploy/blob/canary/CONTRIBUTING.md#pull-request
  • You have tested this PR in your local instance. If you have not tested it yet, please do so before submitting. This helps avoid wasting maintainers' time reviewing code that has not been verified by you.

Issues related (if applicable)

Screenshots (if applicable)

Greptile Summary

Fixed stale closure issue by converting setServerQuantity calls to use functional setState pattern with prev => callback, ensuring React always provides the latest state value.

  • Changed decrement handler from serverQuantity - 1 to (prev) => prev <= 1 ? prev : prev - 1
  • Changed increment handler from serverQuantity + 1 to (prev) => prev + 1
  • This aligns with other instances in the same file that already use the functional form (lines 731, 753, 865-867, 890)

Confidence Score: 4/5

  • This PR is safe to merge with minor refinement needed
  • The functional setState pattern correctly addresses stale closure issues, which is a React best practice. However, there's one inconsistency where line 1083 still checks the stale serverQuantity value before calling setState - this check is now redundant since the functional setState handles the boundary condition. The button's disabled attribute already prevents invalid states, making this a low-risk issue.
  • apps/dokploy/components/dashboard/settings/billing/show-billing.tsx:1083 - remove redundant stale closure check

Last reviewed commit: 29083c9

(4/5) You can add custom instructions or style guidelines for the agent here!

Direct reads of serverQuantity inside onClick handlers can produce stale
values in closures. Use the callback form setState(prev => ...) so React
always supplies the latest state value.
Copilot AI review requested due to automatic review settings February 22, 2026 13:44
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

1 file reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

@@ -1082,7 +1082,9 @@ export const ShowBilling = () => {
onClick={() => {
if (serverQuantity <= 1) return;
Copy link
Contributor

Choose a reason for hiding this comment

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

this check still uses the stale serverQuantity value from the closure - if you're fixing stale closures, this line should be removed since the functional setState now handles the boundary check

Suggested change
if (serverQuantity <= 1) return;

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes stale closure issues in the billing component's server quantity controls by converting direct state reads to functional setState callbacks. The changes specifically target the "legacy" pricing tier's increment and decrement handlers.

Changes:

  • Updated the decrement handler to use functional setState with prev parameter
  • Updated the increment handler to use functional setState with prev parameter

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 1083 to 1084
if (serverQuantity <= 1) return;

Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

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

The guard condition still reads from the stale serverQuantity closure variable instead of using the current state value. This defeats the purpose of the functional setState fix. This line should be removed since the guard is now handled inside the setState callback on line 1086.

Suggested change
if (serverQuantity <= 1) return;

Copilot uses AI. Check for mistakes.
Comment on lines +1085 to +1087
setServerQuantity((prev) =>
prev <= 1 ? prev : prev - 1,
);
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

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

The decrement handler implementation is inconsistent with the existing patterns in the codebase. The "hobby" section (line 731) and "startup" section (line 865) use Math.max() for a cleaner implementation: setServerQuantity((q) => Math.max(1, q - 1)). Consider adopting the same pattern here for consistency.

Copilot uses AI. Check for mistakes.
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.

1 participant