설명
- 기본적으로 리액트에서는 부모 컴포넌트에서 자식 컴포넌트 내의 state 접근이 제한적이다.
- 이를 보완하기 위해 리액트에서 제공하는 훅이
useImperativeHandle
예제 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>
);
}