How to Update Input Value without onChange in React?

I’ve been working on a React component where I have implemented handleStepUp and handleStepDown functions for incrementing and decrementing values. However, I’m facing a challenge with updating the input value within these functions since they don’t have access to an event like onChange. The onChange event is crucial for updating the input value in React, but I can’t directly use it within handleStepUp or handleStepDown.

import * as React from 'react';

import { Input } from '@/components/shadcnui/input';

export interface InputProps
  extends React.InputHTMLAttributes<HTMLInputElement> {
  onChange?: (event: ChangeEventExtendedProps) => void;
  defaultValue?: string;
  step?: number;
  onStepUp?: () => void; // Menambah properti onStepUp
  onStepDown?: () => void; // Menambah properti onStepDown
}

export interface CustomChangeEvent {
  target: { floatValue: number; formattedValue: string; value: string };
}
export interface ChangeEventExtendedProps
  extends React.ChangeEvent<HTMLInputElement> {
  target: HTMLInputElement & {
    floatValue: number | undefined;
    formattedValue: string | undefined;
  };
}

function formatNumber(number: number): string {
  const converted = number.toLocaleString(); // returns a number as a string, using local language format. 1000 => 1,000
  return converted;
}

export const NumberInputFormat = React.forwardRef<HTMLInputElement, InputProps>(
  (props, ref) => {
    const {
      onChange,
      defaultValue = 0,
      step = 1,
      onStepUp,
      onStepDown,
      min,
      max = 10,
    } = props;

    const [formattedValue, setFormattedValue] = React.useState<string>(
      defaultValue.toString()
    );

    // ketika kita mempassing sebuah fungsi ke dalam sebuah props,
    // ada baiknya kita buatkan dia useCallback sehingga fungsi
    // itu tidak dibuat kembali ketika proses re-render
    const handleChange = React.useCallback(
      (event: ChangeEventExtendedProps) => {
        const stringValue = event.target.value;

        const numericValue = stringValue
          ? parseFloat(stringValue.replace(/,/g, ''))
          : 0;

        const formattedStringValue = formatNumber(numericValue);
        setFormattedValue(formattedStringValue);
        onChange?.({
          ...event,
          target: {
            ...event.target,
            floatValue: numericValue,
            formattedValue: formattedStringValue,
            value: stringValue,
          },
        });
      },
      [onChange]
    );

    const handleStepUp = () => {
      const stringValue = formattedValue.replace(/,/g, '');
      const floatValue = parseFloat(stringValue) + step;
      const formattedStringValue = formatNumber(floatValue);
      setFormattedValue(formattedStringValue);

      onStepUp?.(); // Memanggil onStepUp jika diberikan
    };

    const handleStepDown = () => {
      const stringValue = formattedValue.replace(/,/g, '');
      const floatValue = parseFloat(stringValue) - step;
      const formattedStringValue = formatNumber(floatValue);
      setFormattedValue(formattedStringValue);

      onStepDown?.(); // Memanggil onStepUp jika diberikan
    };

    return (
      <>
        <button type="button" onClick={handleStepUp}>
          +
        </button>
        <button type="button" onClick={handleStepDown}>
          -
        </button>
        <Input
          {...props}
          value={formattedValue}
          onChange={handleChange}
          ref={ref}
          type="text"
        />
      </>
    );
  }
);

NumberInputFormat.displayName = 'NumberInputFormat';

What would be the best approach to update the input value without relying on the onChange event? Is there a way to achieve this while maintaining the React best practices? Any insights or alternative methods would be greatly appreciated. Thank you!