설명

예제 1: 기본 구성

import { useRealtimeState } from '@common/hooks/sample/use-realtime-state';
import { useEffect, useImperativeHandle, useRef } from 'react';

export interface InputHandler<T = string> {
  getValue: () => T | undefined;
  setValue: (value: T) => void;
}

export interface InputProps<T = string> {
  ref?: React.Ref<InputHandler<T>>;
}

export function Input<T = string>({ ref }: InputProps<T>) {
  const [value, valueRef, setValue, getValue] = useRealtimeState<T>();

  useImperativeHandle(ref, () => ({
    getValue,
    setValue,
  }));

  return <input />;
}

export function Page() {
  const inputRef = useRef<InputHandler<string>>(null);

  return (
    <div>
      <Input ref={inputRef} />
    </div>
  );
}

예제 2: Props 약간 확장

import { useRealtimeState } from '@common/hooks/sample/use-realtime-state';
import { useEffect, useImperativeHandle, useRef } from 'react';

export interface InputHandler<T = string> {
  getValue: () => T | undefined;
  setValue: (value: T) => void;
}

export interface InputProps<T = string> {
  ref?: React.Ref<InputHandler<T>>;
  onChange?: (value: T) => void;
}

export function Input<T = string>({ ref, onChange }: InputProps<T>) {
  const [value, valueRef, setValue, getValue] = useRealtimeState<T>();

  useImperativeHandle(ref, () => ({
    getValue,
    setValue,
  }));

  const handleChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
    onChange?.(e.target.value as T);
  };

  return <input onChange={handleChange} />;
}

export function Page() {
  const inputRef = useRef<InputHandler<string>>(null);

  useEffect(() => {
    inputRef.current?.getValue();
  }, []);

  const handleChange: React.ComponentProps<typeof Input>['onChange'] = (value) => {};

  return (
    <div>
      <Input ref={inputRef} onChange={handleChange} />
    </div>
  );
}

예제 3: variation 추가

import { useRealtimeState } from '@common/hooks/sample/use-realtime-state';
import { useEffect, useImperativeHandle, useRef } from 'react';

export interface InputHandler<T = string> {
  getValue: () => T | undefined;
  setValue: (value: T) => void;
}

export interface InputProps<T = string> {
  variation?: 'default' | 'number';
  ref?: React.Ref<InputHandler<T>>;
  onChange?: (value: T) => void;
}

export function Input<T = string>({ variation = 'default', ref, onChange }: InputProps<T>) {
  const [value, valueRef, setValue, getValue] = useRealtimeState<T>();

  useImperativeHandle(ref, () => ({
    getValue,
    setValue,
  }));

  const handleChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
    onChange?.(e.target.value as T);
  };

  return <input onChange={handleChange} />;
}

Input.Number = function (props: Omit<InputProps<number>, 'variation'>) {
  return <Input variation="number" {...props} />;
};

export function Page() {
  const inputRef = useRef<InputHandler<string>>(null);

  useEffect(() => {
    inputRef.current?.getValue();
  }, []);

  const handleChange: React.ComponentProps<typeof Input>['onChange'] = (value) => {};

  return (
    <div>
      <Input ref={inputRef} onChange={(val) => {}} />
      <Input.Number onChange={(val) => {}} />
    </div>
  );
}