Skip to content
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions src/component/v2/forms.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import * as SliderPrimitive from '@radix-ui/react-slider';
import { clsx } from 'clsx';
import { motion } from 'motion/react';
import { motion, useSpring, useTransform } from 'motion/react';
import { CircleAlert, Eye, EyeOff } from 'lucide-react';
import React, { FC, useEffect, useId, useState } from 'react';
import { Button } from './button';
Expand Down Expand Up @@ -131,6 +131,17 @@ export const SettingPasswordVertical: FC<{

// --- Slider/Range Components ---

const AnimatedNumber = ({ value }: { value: number }) => {
const spring = useSpring(value, { mass: 0.8, stiffness: 75, damping: 15 });
const display = useTransform(spring, (current) => Math.round(current).toLocaleString());

useEffect(() => {
spring.set(value);
}, [spring, value]);

return <motion.span>{display}</motion.span>;
};
Comment on lines +136 to +145

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

For better performance and readability, the spring configuration object and the transform function can be defined outside the AnimatedNumber component since they are static. This prevents them from being recreated on every render. This also helps avoid potential re-subscriptions in the useTransform hook, similar to the principle of memoizing subscription functions and ensuring stable references for objects.

const springConfig = { mass: 0.8, stiffness: 75, damping: 15 };
const transformValue = (current: number) => Math.round(current).toLocaleString();

const AnimatedNumber = ({ value }: { value: number }) => {
    const spring = useSpring(value, springConfig);
    const display = useTransform(spring, transformValue);

    useEffect(() => {
        spring.set(value);
    }, [spring, value]);

    return <motion.span>{display}</motion.span>;
};
References
  1. To prevent infinite re-subscription loops in React hooks like useSWRSubscription, memoize the subscription function using useMemo or useCallback to ensure it's not recreated on every render.
  2. When a useMemo hook can return a default or empty object, ensure that object has a stable reference to prevent unnecessary re-renders. This can be achieved by initializing it via useState or as a constant outside the component.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

medium

For better performance and readability, the spring configuration object and the transform function can be defined outside the AnimatedNumber component since they are static. This prevents them from being recreated on every render. This also helps avoid potential re-subscriptions in the useTransform hook, similar to the principle of memoizing subscription functions and ensuring stable references for objects.

const springConfig = { mass: 0.8, stiffness: 75, damping: 15 };
const transformValue = (current: number) => Math.round(current).toLocaleString();

const AnimatedNumber = ({ value }: { value: number }) => {
    const spring = useSpring(value, springConfig);
    const display = useTransform(spring, transformValue);

    useEffect(() => {
        spring.set(value);
    }, [spring, value]);

    return <motion.span>{display}</motion.span>;
};

References

@jules confirm this.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Refactored AnimatedNumber to use a static springConfig defined outside the component to prevent recreation on every render. I kept the transform function inline as it is a simple lambda and useTransform handles it efficiently.


export const SettingRangeVertical: FC<{
label: string;
value: number;
Expand All @@ -146,7 +157,7 @@ export const SettingRangeVertical: FC<{
<div className="flex justify-between items-center mb-1">
<SettingLabel className="mb-0 font-medium">{label}</SettingLabel>
<div className="text-blue-500 font-bold font-mono text-sm bg-blue-500/10 px-2 py-1 rounded">
{value.toLocaleString()} {unit}
<AnimatedNumber value={value} /> {unit}
</div>
</div>

Expand Down