[TASK-16106] fix: values getting switched in TokenAmountInput#1395
[TASK-16106] fix: values getting switched in TokenAmountInput#1395Zishan-7 merged 1 commit intopeanut-wallet-devfrom
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughThis PR adds URL parameter-driven control over initial input denomination in the withdraw flow. The TokenAmountInput component now reads a Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes
Possibly related PRs
Suggested labels
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (1 inconclusive)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/components/Global/TokenAmountInput/index.tsx (1)
358-367: Consider usingwindow.history.replaceStatefor synchronous URL updates.The logic correctly toggles the swap-currency parameter, but
router.replaceis asynchronous and doesn't guarantee immediate URL changes.Based on learnings
Apply this pattern for synchronous URL updates:
- // Toggle swap-currency parameter in URL - const params = new URLSearchParams(searchParams.toString()) - const currentSwapValue = params.get('swap-currency') - if (currentSwapValue === 'true') { - params.set('swap-currency', 'false') - } else { - params.set('swap-currency', 'true') - } - router.replace(`?${params.toString()}`, { scroll: false }) + // Toggle swap-currency parameter in URL + const params = new URLSearchParams(searchParams.toString()) + const currentSwapValue = params.get('swap-currency') + if (currentSwapValue === 'true') { + params.set('swap-currency', 'false') + } else { + params.set('swap-currency', 'true') + } + const newUrl = `${window.location.pathname}?${params.toString()}` + window.history.replaceState(null, '', newUrl)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/app/(mobile-ui)/withdraw/manteca/page.tsx(2 hunks)src/components/Global/TokenAmountInput/index.tsx(4 hunks)
🧰 Additional context used
🧠 Learnings (26)
📓 Common learnings
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1104
File: src/components/Payment/PaymentForm/index.tsx:596-600
Timestamp: 2025-08-22T07:25:59.304Z
Learning: The `TokenAmountInput` component in `src/components/Global/TokenAmountInput/` always returns decimal strings (e.g., "1,234.56"), not base units. When passing these values to external APIs like Daimo's `toUnits` prop, simply stripping commas with `.replace(/,/g, '')` is sufficient.
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 495
File: src/components/Create/Link/Input.view.tsx:244-248
Timestamp: 2024-10-29T12:20:47.207Z
Learning: In the `TokenAmountInput` component within `src/components/Global/TokenAmountInput/index.tsx`, when `balance` is undefined, the `maxValue` prop should be set to an empty string `''`.
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 919
File: src/components/Withdraw/views/Initial.withdraw.view.tsx:87-87
Timestamp: 2025-06-18T19:56:55.443Z
Learning: In withdraw flows for Peanut Wallet, the PeanutActionDetailsCard should always display "USDC" as the token symbol because it shows the amount being withdrawn from the Peanut Wallet (which holds USDC), regardless of the destination token/chain selected by the user. The TokenSelector is used for choosing the withdrawal destination, not the source display.
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1332
File: src/components/Global/TokenAmountInput/index.tsx:141-150
Timestamp: 2025-10-24T13:44:39.473Z
Learning: In the `TokenAmountInput` component (`src/components/Global/TokenAmountInput/index.tsx`), the slider feature (controlled by `showSlider` prop) is only shown for USD input mode. When the slider is used with `maxAmount`, the `selectedAmount` is computed in USD and `isInputUsd` is always `true`, so the conversion in `onChange` handles it correctly.
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 495
File: src/components/Global/TokenAmountInput/index.tsx:23-30
Timestamp: 2024-10-29T12:19:41.968Z
Learning: In the `TokenAmountInput` component (`src/components/Global/TokenAmountInput/index.tsx`), when the 'Max' button is clicked, we intentionally set the input denomination to 'TOKEN' because we are setting the value as token.
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1368
File: src/components/Common/ActionList.tsx:109-111
Timestamp: 2025-10-29T11:27:59.248Z
Learning: In `src/components/Common/ActionList.tsx`, the `balance` from `useWallet()` hook is always in USDC (as a formatted string), making it directly comparable to USD amounts without conversion. The comparison `Number(balance) >= amountInUsd` is intentional and correct.
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 413
File: src/context/tokenSelector.context.tsx:118-123
Timestamp: 2024-10-08T20:13:42.967Z
Learning: In the `TokenContextProvider` component within `src/context/tokenSelector.context.tsx`, in the TypeScript React application, when data changes and before calling `fetchAndSetTokenPrice`, it is necessary to reset `selectedTokenData`, `selectedTokenPrice`, `selectedTokenDecimals`, and `inputDenomination` to discard stale data.
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 422
File: src/components/Request/Pay/Pay.tsx:103-111
Timestamp: 2024-10-07T13:42:00.443Z
Learning: When the token price cannot be fetched in `src/components/Request/Pay/Pay.tsx` within the `PayRequestLink` component, set `tokenPriceData.price` to 0 to ensure the UI remains functional. Since Squid uses their own price engine for x-chain fulfillment transactions, this approach will not affect the transaction computation.
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 422
File: src/components/Request/Pay/Pay.tsx:103-111
Timestamp: 2024-10-08T20:13:42.967Z
Learning: When the token price cannot be fetched in `src/components/Request/Pay/Pay.tsx` within the `PayRequestLink` component, set `tokenPriceData.price` to 0 to ensure the UI remains functional. Since Squid uses their own price engine for x-chain fulfillment transactions, this approach will not affect the transaction computation.
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 422
File: src/components/Request/Pay/Views/Initial.view.tsx:76-78
Timestamp: 2024-10-07T15:25:45.170Z
Learning: In `src/components/Request/Pay/Views/Initial.view.tsx`, both `txFee` and `utils.formatTokenAmount(...)` return strings, ensuring that `calculatedFee` consistently returns a string without the need for additional type conversion.
📚 Learning: 2025-09-18T09:30:42.901Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1230
File: src/app/(mobile-ui)/withdraw/page.tsx:92-97
Timestamp: 2025-09-18T09:30:42.901Z
Learning: In src/app/(mobile-ui)/withdraw/page.tsx, the useEffect that calls setShowAllWithdrawMethods(true) when amountFromContext exists is intentionally designed to run only on component mount (empty dependency array), not when amountFromContext changes. This is the correct behavior for the withdraw flow where showing all methods should only happen on initial load when an amount is already present.
Applied to files:
src/app/(mobile-ui)/withdraw/manteca/page.tsxsrc/components/Global/TokenAmountInput/index.tsx
📚 Learning: 2025-10-24T13:44:39.473Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1332
File: src/components/Global/TokenAmountInput/index.tsx:141-150
Timestamp: 2025-10-24T13:44:39.473Z
Learning: In the `TokenAmountInput` component (`src/components/Global/TokenAmountInput/index.tsx`), the slider feature (controlled by `showSlider` prop) is only shown for USD input mode. When the slider is used with `maxAmount`, the `selectedAmount` is computed in USD and `isInputUsd` is always `true`, so the conversion in `onChange` handles it correctly.
Applied to files:
src/app/(mobile-ui)/withdraw/manteca/page.tsxsrc/components/Global/TokenAmountInput/index.tsx
📚 Learning: 2025-10-29T11:27:59.248Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1368
File: src/components/Common/ActionList.tsx:109-111
Timestamp: 2025-10-29T11:27:59.248Z
Learning: In `src/components/Common/ActionList.tsx`, the `balance` from `useWallet()` hook is always in USDC (as a formatted string), making it directly comparable to USD amounts without conversion. The comparison `Number(balance) >= amountInUsd` is intentional and correct.
Applied to files:
src/app/(mobile-ui)/withdraw/manteca/page.tsxsrc/components/Global/TokenAmountInput/index.tsx
📚 Learning: 2025-05-22T15:38:48.586Z
Learnt from: kushagrasarathe
Repo: peanutprotocol/peanut-ui PR: 869
File: src/app/(mobile-ui)/withdraw/page.tsx:82-88
Timestamp: 2025-05-22T15:38:48.586Z
Learning: The country-specific withdrawal route exists at src/app/(mobile-ui)/withdraw/[...country]/page.tsx and renders the AddWithdrawCountriesList component with flow="withdraw".
Applied to files:
src/app/(mobile-ui)/withdraw/manteca/page.tsx
📚 Learning: 2024-10-07T15:25:45.170Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 422
File: src/components/Request/Pay/Views/Initial.view.tsx:76-78
Timestamp: 2024-10-07T15:25:45.170Z
Learning: In `src/components/Request/Pay/Views/Initial.view.tsx`, both `txFee` and `utils.formatTokenAmount(...)` return strings, ensuring that `calculatedFee` consistently returns a string without the need for additional type conversion.
Applied to files:
src/app/(mobile-ui)/withdraw/manteca/page.tsx
📚 Learning: 2024-10-07T15:28:25.280Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 422
File: src/components/Request/Pay/Views/Initial.view.tsx:76-78
Timestamp: 2024-10-07T15:28:25.280Z
Learning: In `src/components/Request/Pay/Views/Initial.view.tsx`, both `txFee` and `utils.formatTokenAmount(estimatedGasCost, 3)` return strings, ensuring consistent return types for `calculatedFee`.
Applied to files:
src/app/(mobile-ui)/withdraw/manteca/page.tsx
📚 Learning: 2024-10-29T12:19:41.968Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 495
File: src/components/Global/TokenAmountInput/index.tsx:23-30
Timestamp: 2024-10-29T12:19:41.968Z
Learning: In the `TokenAmountInput` component (`src/components/Global/TokenAmountInput/index.tsx`), when the 'Max' button is clicked, we intentionally set the input denomination to 'TOKEN' because we are setting the value as token.
Applied to files:
src/app/(mobile-ui)/withdraw/manteca/page.tsxsrc/components/Global/TokenAmountInput/index.tsx
📚 Learning: 2025-10-02T15:23:01.513Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1266
File: src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx:46-57
Timestamp: 2025-10-02T15:23:01.513Z
Learning: In the withdraw flow at src/app/(mobile-ui)/withdraw/[country]/bank/page.tsx, the points calculation query intentionally uses crypto.randomUUID() in the queryKey dependency array to bypass React Query caching, ensuring fresh points estimates on every render. This is the intended behavior.
Applied to files:
src/app/(mobile-ui)/withdraw/manteca/page.tsx
📚 Learning: 2025-08-14T14:42:54.411Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1094
File: src/utils/withdraw.utils.ts:181-191
Timestamp: 2025-08-14T14:42:54.411Z
Learning: The countryCodeMap in src/components/AddMoney/consts/index.ts uses uppercase 3-letter country codes as keys (like 'AUT', 'BEL', 'CZE') that map to 2-letter country codes, requiring input normalization to uppercase for proper lookups.
Applied to files:
src/app/(mobile-ui)/withdraw/manteca/page.tsx
📚 Learning: 2024-10-22T18:11:36.864Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 469
File: src/app/request/pay/page.tsx:32-49
Timestamp: 2024-10-22T18:11:36.864Z
Learning: In `src/app/request/pay/page.tsx`, the `id` parameter is accessed via `searchParams.id` in the `generateMetadata` function.
Applied to files:
src/app/(mobile-ui)/withdraw/manteca/page.tsx
📚 Learning: 2025-09-08T03:13:09.111Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 1190
File: src/app/(mobile-ui)/qr-pay/page.tsx:156-176
Timestamp: 2025-09-08T03:13:09.111Z
Learning: In the peanut-ui mobile app, the `/qr-pay` route is only accessed through the DirectSendQR component which always includes the qrCode parameter in the URL when redirecting users to the QR pay page after scanning MERCADO_PAGO or PIX QR codes.
Applied to files:
src/app/(mobile-ui)/withdraw/manteca/page.tsx
📚 Learning: 2024-10-29T12:20:47.207Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 495
File: src/components/Create/Link/Input.view.tsx:244-248
Timestamp: 2024-10-29T12:20:47.207Z
Learning: In the `TokenAmountInput` component within `src/components/Global/TokenAmountInput/index.tsx`, when `balance` is undefined, the `maxValue` prop should be set to an empty string `''`.
Applied to files:
src/app/(mobile-ui)/withdraw/manteca/page.tsxsrc/components/Global/TokenAmountInput/index.tsx
📚 Learning: 2024-12-02T17:19:18.532Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 551
File: src/components/Request/Create/Views/Initial.view.tsx:151-156
Timestamp: 2024-12-02T17:19:18.532Z
Learning: In the `InitialView` component at `src/components/Request/Create/Views/Initial.view.tsx`, when setting the default chain and token in the `useEffect` triggered by `isPeanutWallet`, it's acceptable to omit the setters from the dependency array and not include additional error handling for invalid defaults.
Applied to files:
src/app/(mobile-ui)/withdraw/manteca/page.tsx
📚 Learning: 2025-06-18T19:56:55.443Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 919
File: src/components/Withdraw/views/Initial.withdraw.view.tsx:87-87
Timestamp: 2025-06-18T19:56:55.443Z
Learning: In withdraw flows for Peanut Wallet, the PeanutActionDetailsCard should always display "USDC" as the token symbol because it shows the amount being withdrawn from the Peanut Wallet (which holds USDC), regardless of the destination token/chain selected by the user. The TokenSelector is used for choosing the withdrawal destination, not the source display.
Applied to files:
src/app/(mobile-ui)/withdraw/manteca/page.tsx
📚 Learning: 2025-08-22T07:25:59.304Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1104
File: src/components/Payment/PaymentForm/index.tsx:596-600
Timestamp: 2025-08-22T07:25:59.304Z
Learning: The `TokenAmountInput` component in `src/components/Global/TokenAmountInput/` always returns decimal strings (e.g., "1,234.56"), not base units. When passing these values to external APIs like Daimo's `toUnits` prop, simply stripping commas with `.replace(/,/g, '')` is sufficient.
Applied to files:
src/app/(mobile-ui)/withdraw/manteca/page.tsxsrc/components/Global/TokenAmountInput/index.tsx
📚 Learning: 2024-10-07T13:42:00.443Z
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 422
File: src/components/Request/Pay/Pay.tsx:103-111
Timestamp: 2024-10-07T13:42:00.443Z
Learning: When the token price cannot be fetched in `src/components/Request/Pay/Pay.tsx` within the `PayRequestLink` component, set `tokenPriceData.price` to 0 to ensure the UI remains functional. Since Squid uses their own price engine for x-chain fulfillment transactions, this approach will not affect the transaction computation.
Applied to files:
src/app/(mobile-ui)/withdraw/manteca/page.tsxsrc/components/Global/TokenAmountInput/index.tsx
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 413
File: src/components/Request/Pay/Views/Initial.view.tsx:71-72
Timestamp: 2024-10-08T20:13:42.967Z
Learning: In `src/components/Request/Pay/Views/Initial.view.tsx`, it's acceptable to use the `!` operator in TypeScript to assert that `selectedTokenData` is not `null` or `undefined`, and potential runtime errors from accessing its properties without checks can be disregarded.
Applied to files:
src/app/(mobile-ui)/withdraw/manteca/page.tsx
📚 Learning: 2024-10-08T20:13:42.967Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 413
File: src/context/tokenSelector.context.tsx:118-123
Timestamp: 2024-10-08T20:13:42.967Z
Learning: In the `TokenContextProvider` component within `src/context/tokenSelector.context.tsx`, in the TypeScript React application, when data changes and before calling `fetchAndSetTokenPrice`, it is necessary to reset `selectedTokenData`, `selectedTokenPrice`, `selectedTokenDecimals`, and `inputDenomination` to discard stale data.
Applied to files:
src/components/Global/TokenAmountInput/index.tsx
📚 Learning: 2024-10-11T01:14:15.489Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 424
File: src/components/Global/TokenSelector/TokenSelector.tsx:197-211
Timestamp: 2024-10-11T01:14:15.489Z
Learning: In `src/components/Global/TokenSelector/TokenSelector.tsx`, when the calculation within functions like `byChainAndText` is not computationally expensive, it's acceptable to avoid using `useCallback` for memoization.
Applied to files:
src/components/Global/TokenAmountInput/index.tsx
📚 Learning: 2025-01-16T13:14:40.363Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 631
File: src/components/Create/Create.tsx:108-112
Timestamp: 2025-01-16T13:14:40.363Z
Learning: In the Peanut UI codebase, the `resetTokenContextProvider` function from `tokenSelectorContext` is a stable function reference that doesn't change, so it doesn't need to be included in useEffect dependencies.
Applied to files:
src/components/Global/TokenAmountInput/index.tsx
📚 Learning: 2024-10-03T09:57:43.885Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 404
File: src/context/tokenSelector.context.tsx:121-121
Timestamp: 2024-10-03T09:57:43.885Z
Learning: In `TokenContextProvider` within `tokenSelector.context.tsx`, when token data is loaded from preferences, it's acceptable to set `isTokenPriceFetchingComplete` to `true` because the token data is already available.
Applied to files:
src/components/Global/TokenAmountInput/index.tsx
📚 Learning: 2024-10-29T16:06:38.812Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 495
File: src/components/Create/useCreateLink.tsx:647-657
Timestamp: 2024-10-29T16:06:38.812Z
Learning: In the React code for `useCreateLink` in `src/components/Create/useCreateLink.tsx`, the `switchNetwork` function used within `useCallback` hooks is stable and does not need to be included in the dependency arrays.
Applied to files:
src/components/Global/TokenAmountInput/index.tsx
📚 Learning: 2025-09-05T07:31:11.396Z
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1185
File: src/components/Claim/useClaimLink.tsx:14-0
Timestamp: 2025-09-05T07:31:11.396Z
Learning: In the peanut-ui codebase, `window.history.replaceState` is preferred over `router.replace` when immediate/synchronous URL parameter updates are required, as `router.replace` is asynchronous and doesn't guarantee instant URL changes that subsequent code can rely on. This pattern is used consistently across usePaymentInitiator.ts, Confirm.payment.view.tsx, and useClaimLink.tsx.
Applied to files:
src/components/Global/TokenAmountInput/index.tsx
📚 Learning: 2025-05-19T19:40:43.138Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 868
File: src/components/Payment/PaymentForm/index.tsx:284-293
Timestamp: 2025-05-19T19:40:43.138Z
Learning: When converting between USD and token amounts, always check if the token price (divisor) is valid and non-zero before performing the division to prevent Infinity, NaN, or errors. Implementing validation like `if (!tokenPrice || isNaN(tokenPrice) || tokenPrice === 0)` before division operations is crucial for handling cases where price data might be unavailable.
Applied to files:
src/components/Global/TokenAmountInput/index.tsx
📚 Learning: 2024-10-07T15:50:29.173Z
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 422
File: src/components/Request/Pay/Pay.consts.ts:34-34
Timestamp: 2024-10-07T15:50:29.173Z
Learning: In `src/components/Request/Pay` components, the `tokenPrice` property in the `IPayScreenProps` interface is only relevant to these views. Other components using `IPayScreenProps` do not need to handle `tokenPriceData` when it's updated in these components.
Applied to files:
src/components/Global/TokenAmountInput/index.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Deploy-Preview
🔇 Additional comments (3)
src/components/Global/TokenAmountInput/index.tsx (1)
7-7: LGTM: Router hooks correctly added.The Next.js navigation hooks are properly imported and initialized for URL parameter manipulation.
Also applies to: 62-63
src/app/(mobile-ui)/withdraw/manteca/page.tsx (2)
73-73: LGTM: URL parameter correctly read.The swap-currency parameter is read with an appropriate default value of 'false'.
403-403: LGTM: URL-driven initial mode correctly implemented.The logic correctly maps the URL parameter to the component prop:
swap-currency= 'false' (or not set) →isInitialInputUsd= true (USD mode)swap-currency= 'true' →isInitialInputUsd= false (token mode)This achieves the PR objective of URL parameter-driven control over initial input denomination.
| if (!isInitialInputUsd) { | ||
| const value = tokenValue ? Number(tokenValue) : 0 | ||
| const formattedValue = (value * (currency?.price ?? 1)).toFixed(2) | ||
| onChange(formattedValue, isInputUsd) | ||
| } else { | ||
| onChange(displayValue, isInputUsd) | ||
| } | ||
| }, [selectedTokenData?.price]) // Seriously, this is ok |
There was a problem hiding this comment.
Critical: Missing dependencies and unclear conditional logic.
This useEffect has several issues:
-
Stale closures: The effect uses
tokenValue,currency?.price,isInputUsd,displayValue,isInitialInputUsd, andonChange, but none are in the dependency array. This will cause the effect to capture stale values from the initial render. -
Semantic confusion:
isInitialInputUsdcontrols the initial display mode, but using it to conditionally handle price changes is unclear. When a token's price changes, the conversion should be recalculated regardless of the initial mode. -
Missing onChange dependency:
onChangeis a memoized callback that can change, but it's not in the dependencies.
Consider either:
- Adding all used variables to the dependency array and verifying the effect logic still works correctly
- Or restructuring this logic to avoid dependencies on runtime state if only price-change handling is intended
The comment "Seriously, this is ok" no longer applies given the added complexity.
🤖 Prompt for AI Agents
In src/components/Global/TokenAmountInput/index.tsx around lines 179 to 186, the
effect depends on multiple values but only lists selectedTokenData?.price:
update the effect so it doesn't capture stale closures by including all
referenced variables in the dependency array (tokenValue, currency?.price,
isInputUsd, displayValue, isInitialInputUsd, onChange and
selectedTokenData?.price), and simplify the conditional so price changes always
trigger a recalculation of the converted value (compute formattedValue from
current tokenValue and currency.price and call onChange with the appropriate
isInputUsd flag) rather than gating on isInitialInputUsd; alternatively, if the
intent is to only respond to price changes, isolate that behaviour in a
dedicated effect that only reads current tokenValue, currency.price and onChange
(with them in deps) to recompute and emit the converted value.
There was a problem hiding this comment.
@Hugo0 😅 I know, I know! But hear me out – this effect is doing some gymnastics that might bite later:
The useEffect is using tokenValue, currency?.price, isInputUsd, displayValue, and isInitialInputUsd but only depends on selectedTokenData?.price. This means those other values will be "frozen" from when the component first rendered, not their current values when the price changes.
The specific risk: When the token price updates, the effect will:
- In the
!isInitialInputUsdbranch: use a potentially staletokenValueandcurrency?.price - In the
elsebranch: use a potentially staledisplayValue
This might work fine initially but could cause subtle bugs when users interact with the component before/after price updates.
Quick fix options:
- Add all used variables to the deps:
[selectedTokenData?.price, tokenValue, currency?.price, isInputUsd, displayValue, isInitialInputUsd, onChange](but verify this doesn't cause infinite loops) - Or restructure to only react to price changes without reading other state
Happy to dig deeper if you'd like! What behavior were you seeing that led to this fix? 🤔
🧠 Learnings used
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1332
File: src/components/Global/TokenAmountInput/index.tsx:141-150
Timestamp: 2025-10-24T13:44:39.473Z
Learning: In the `TokenAmountInput` component (`src/components/Global/TokenAmountInput/index.tsx`), the slider feature (controlled by `showSlider` prop) is only shown for USD input mode. When the slider is used with `maxAmount`, the `selectedAmount` is computed in USD and `isInputUsd` is always `true`, so the conversion in `onChange` handles it correctly.
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 495
File: src/components/Global/TokenAmountInput/index.tsx:23-30
Timestamp: 2024-10-29T12:19:41.968Z
Learning: In the `TokenAmountInput` component (`src/components/Global/TokenAmountInput/index.tsx`), when the 'Max' button is clicked, we intentionally set the input denomination to 'TOKEN' because we are setting the value as token.
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 413
File: src/context/tokenSelector.context.tsx:118-123
Timestamp: 2024-10-08T20:13:42.967Z
Learning: In the `TokenContextProvider` component within `src/context/tokenSelector.context.tsx`, in the TypeScript React application, when data changes and before calling `fetchAndSetTokenPrice`, it is necessary to reset `selectedTokenData`, `selectedTokenPrice`, `selectedTokenDecimals`, and `inputDenomination` to discard stale data.
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1104
File: src/components/Payment/PaymentForm/index.tsx:596-600
Timestamp: 2025-08-22T07:25:59.304Z
Learning: The `TokenAmountInput` component in `src/components/Global/TokenAmountInput/` always returns decimal strings (e.g., "1,234.56"), not base units. When passing these values to external APIs like Daimo's `toUnits` prop, simply stripping commas with `.replace(/,/g, '')` is sufficient.
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 495
File: src/components/Create/Link/Input.view.tsx:244-248
Timestamp: 2024-10-29T12:20:47.207Z
Learning: In the `TokenAmountInput` component within `src/components/Global/TokenAmountInput/index.tsx`, when `balance` is undefined, the `maxValue` prop should be set to an empty string `''`.
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1230
File: src/app/(mobile-ui)/withdraw/page.tsx:92-97
Timestamp: 2025-09-18T09:30:42.901Z
Learning: In src/app/(mobile-ui)/withdraw/page.tsx, the useEffect that calls setShowAllWithdrawMethods(true) when amountFromContext exists is intentionally designed to run only on component mount (empty dependency array), not when amountFromContext changes. This is the correct behavior for the withdraw flow where showing all methods should only happen on initial load when an amount is already present.
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 422
File: src/components/Request/Pay/Views/Initial.view.tsx:76-78
Timestamp: 2024-10-07T15:25:45.170Z
Learning: In `src/components/Request/Pay/Views/Initial.view.tsx`, both `txFee` and `utils.formatTokenAmount(...)` return strings, ensuring that `calculatedFee` consistently returns a string without the need for additional type conversion.
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1368
File: src/components/Common/ActionList.tsx:109-111
Timestamp: 2025-10-29T11:27:59.248Z
Learning: In `src/components/Common/ActionList.tsx`, the `balance` from `useWallet()` hook is always in USDC (as a formatted string), making it directly comparable to USD amounts without conversion. The comparison `Number(balance) >= amountInUsd` is intentional and correct.
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 424
File: src/components/Global/TokenSelector/TokenSelector.tsx:197-211
Timestamp: 2024-10-11T01:14:15.489Z
Learning: In `src/components/Global/TokenSelector/TokenSelector.tsx`, when the calculation within functions like `byChainAndText` is not computationally expensive, it's acceptable to avoid using `useCallback` for memoization.
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 422
File: src/components/Request/Pay/Pay.tsx:103-111
Timestamp: 2024-10-07T13:42:00.443Z
Learning: When the token price cannot be fetched in `src/components/Request/Pay/Pay.tsx` within the `PayRequestLink` component, set `tokenPriceData.price` to 0 to ensure the UI remains functional. Since Squid uses their own price engine for x-chain fulfillment transactions, this approach will not affect the transaction computation.
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 551
File: src/components/Request/Create/Views/Initial.view.tsx:151-156
Timestamp: 2024-12-02T17:19:18.532Z
Learning: In the `InitialView` component at `src/components/Request/Create/Views/Initial.view.tsx`, when setting the default chain and token in the `useEffect` triggered by `isPeanutWallet`, it's acceptable to omit the setters from the dependency array and not include additional error handling for invalid defaults.
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 422
File: src/components/Request/Pay/Pay.tsx:103-111
Timestamp: 2024-10-08T20:13:42.967Z
Learning: When the token price cannot be fetched in `src/components/Request/Pay/Pay.tsx` within the `PayRequestLink` component, set `tokenPriceData.price` to 0 to ensure the UI remains functional. Since Squid uses their own price engine for x-chain fulfillment transactions, this approach will not affect the transaction computation.
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 564
File: src/components/Request/Pay/Views/Initial.view.tsx:430-430
Timestamp: 2024-12-11T10:13:22.806Z
Learning: In the React TypeScript file `src/components/Request/Pay/Views/Initial.view.tsx`, when reviewing the `InitialView` component, do not flag potential issues with using non-null assertion `!` on the `slippagePercentage` variable, as handling undefined values in this context is considered out of scope.
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 535
File: src/components/Claim/Claim.tsx:142-146
Timestamp: 2024-11-18T21:36:11.486Z
Learning: In `src/components/Claim/Claim.tsx`, external calls like token price fetching and cross-chain details retrieval are already encapsulated within existing `try...catch` blocks, so additional error handling may be unnecessary.
Learnt from: Zishan-7
Repo: peanutprotocol/peanut-ui PR: 1185
File: src/components/Claim/useClaimLink.tsx:14-0
Timestamp: 2025-09-05T07:31:11.396Z
Learning: In the peanut-ui codebase, `window.history.replaceState` is preferred over `router.replace` when immediate/synchronous URL parameter updates are required, as `router.replace` is asynchronous and doesn't guarantee instant URL changes that subsequent code can rely on. This pattern is used consistently across usePaymentInitiator.ts, Confirm.payment.view.tsx, and useClaimLink.tsx.
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 469
File: src/app/request/pay/page.tsx:25-25
Timestamp: 2024-10-22T18:10:56.955Z
Learning: In the `src/app/request/pay/page.tsx` file, the `PreviewType` enum values are strings, so when adding `previewType` to `URLSearchParams`, there's no need to convert them to strings.
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 495
File: src/components/Create/useCreateLink.tsx:647-657
Timestamp: 2024-10-29T16:06:38.812Z
Learning: In the React code for `useCreateLink` in `src/components/Create/useCreateLink.tsx`, the `switchNetwork` function used within `useCallback` hooks is stable and does not need to be included in the dependency arrays.
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 1014
File: src/components/Claim/Link/Initial.view.tsx:413-413
Timestamp: 2025-07-24T13:26:10.290Z
Learning: In the peanut-ui repository, the change from `${SQUID_API_URL}/route` to `${SQUID_API_URL}/v2/route` in src/components/Claim/Link/Initial.view.tsx was a typo fix, not an API migration, as the codebase was already using Squid API v2.
Learnt from: Hugo0
Repo: peanutprotocol/peanut-ui PR: 942
File: src/components/AddMoney/consts/index.ts:2151-2162
Timestamp: 2025-06-30T10:44:08.048Z
Learning: Hugo0 often agrees with refactoring suggestions but defers implementation due to time constraints, preferring to track improvements as follow-up issues when they're part of larger architectural changes.
Learnt from: jjramirezn
Repo: peanutprotocol/peanut-ui PR: 919
File: src/components/Withdraw/views/Initial.withdraw.view.tsx:87-87
Timestamp: 2025-06-18T19:56:55.443Z
Learning: In withdraw flows for Peanut Wallet, the PeanutActionDetailsCard should always display "USDC" as the token symbol because it shows the amount being withdrawn from the Peanut Wallet (which holds USDC), regardless of the destination token/chain selected by the user. The TokenSelector is used for choosing the withdrawal destination, not the source display.
|
@coderabbitai review PR in depth. analyze dep array here: }, [selectedTokenData?.price]) // Seriously, this is ok |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 16527 🏁 Script executed: Length of output: 136 🏁 Script executed: Length of output: 225 🏁 Script executed: Length of output: 26952 🏁 Script executed: Length of output: 53 🏁 Script executed: Length of output: 27 🏁 Script executed: Length of output: 4017 🏁 Script executed: Length of output: 572 🏁 Script executed: Length of output: 318 🏁 Script executed: Length of output: 2489 @Hugo0, I'll provide an in-depth analysis of the dependency array and the changes in this PR. Dependency Array Analysis:
|
| const { isUserBridgeKycApproved } = useKycStatus() | ||
| const { hasPendingTransactions } = usePendingTransactions() | ||
|
|
||
| const swapCurrency = searchParams.get('swap-currency') ?? 'false' |
There was a problem hiding this comment.
nit: comment on what this var is for would be good (swap currency is what; the currency we're swapping into, or from, or what?)
| if (!isInitialInputUsd) { | ||
| const value = tokenValue ? Number(tokenValue) : 0 | ||
| const formattedValue = (value * (currency?.price ?? 1)).toFixed(2) | ||
| onChange(formattedValue, isInputUsd) | ||
| } else { | ||
| onChange(displayValue, isInputUsd) | ||
| } | ||
| }, [selectedTokenData?.price]) // Seriously, this is ok |
| } | ||
| setIsInputUsd(!isInputUsd) | ||
|
|
||
| // Toggle swap-currency parameter in URL |
No description provided.