Czy moja implementacja pływającej etykiety pola edycyjnego nie jest zbyt skomplikowana?

0

Hej, mam pytanie bardziej recenzenckie bo zrobilem sobie komponent input z plywajacym labelem w React i chciabym sie tylko dowiedziec czy pod wzgledem samego kodu jest ok bo dziala tak jak nalezy ale mam tendencje do komplikowania rzeczy.

export interface InputProps
  extends React.InputHTMLAttributes<HTMLInputElement> {
  variant?: 'border' | 'inside';
  label?: string;
}

const LabeledInput = React.forwardRef<HTMLInputElement, InputProps>(
  ({ className, type, variant = 'inside', label, ...props }, ref) => {
    const [inputRef, setInputRef] = useState<HTMLInputElement | null>(null);
    const hasValue = inputRef?.value !== '' && inputRef?.value !== '0';
    const [hasFocus, setHasFocus] = useState(false);

    return (
      <div className='relative flex'>
        <input
          onFocus={() => setHasFocus(true)}
          onBlur={() => setHasFocus(false)}
          ref={(node) => {
            setInputRef(node);
            if (typeof ref === 'function') {
              ref(node);
            } else if (ref) {
              ref.current = node;
            }
          }}
          type={type}
          className={cn(
            'flex h-10 w-full rounded-md border-[1px] border-[var(--border-input)] bg-input px-[15px] text-sm transition-all duration-300 file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground hover:shadow-[inset_0_0_0_2px_#767676] focus:border-[var(--accent-8)] focus:shadow-[0_0_0_1.5px_var(--accent-10)] focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50',
            className
          )}
          style={{
            padding:
              variant === 'inside'
                ? hasValue || hasFocus
                  ? '20px 12px 6px'
                  : '12px'
                : '12px',
          }}
          {...props}
        />
        <label
          style={{
            top:
              variant === 'border'
                ? hasValue || hasFocus
                  ? 0
                  : '50%'
                : hasValue || hasFocus
                  ? '10px'
                  : '50%',
            fontSize: hasValue || hasFocus ? '9px' : '14px',
          }}
          className='pointer-events-none absolute left-[7px] -translate-y-[50%] cursor-text rounded-sm bg-background px-[5px] text-muted-foreground transition-all duration-200'
        >
          <TextGradient>{label}</TextGradient>
        </label>
      </div>
    );
  }
);
1
Kokos12345 napisał(a):
const [inputRef, setInputRef] = useState<HTMLInputElement | null>(null);
const hasValue = inputRef?.value !== '' && inputRef?.value !== '0';

Tu jest coś nieprzemyślane.
W samym kodzie komponentu (mam na myśli to, co zwracasz w JSX) używasz tylko hasValue i to hasValue jest lepszym kandydatem na stan. Czyli bardziej tak bym zrobił

const [hasValue, setHasValue] = useState(false);

Natomiast inputRef w ogóle nie powinien być stanem[1], bo używasz inputRef tylko po to, żeby ustawić finalnie hasValue. Czyli wypieprzyłbym inputRef całkowicie, a zamiast:

setInputRef(node);

napisałbym

setHasValue(node?.value !== '' && node?.value !== '0');

czyli przeniósłbym warunek z góry komponentu.

ale mam tendencje do komplikowania rzeczy.

To prawda.

[1] już pomijając to, że trzymanie elementów DOM w stanie jest dość dziwne i tak jakby sprzeczne z filozofią Reacta.

1 użytkowników online, w tym zalogowanych: 0, gości: 1