리액트에서 input태그를 이용해 유저로부터 파일을 받아보겠습니다.
const FileInput = () => {
const [value, setValue] = useState(null);
const handleChange = (e) => {
const nextValue = e.target.files[0];
setValue(nextValue);
};
return (
<div>
<input type="file" onChange={handleChange} />
</div>
);
}
export default FileInput;
잘 출력되는 것을 볼 수 있습니다. 이 때 왜 input 컴포넌트에 value값을 넘겨주지 않는 것일까요?
왜냐하면 파일을 받을 때는 비제어컴포넌트를 사용해야하기 때문입니다.
제어 컴포넌트는 input 태그의 value 속성을 지정
하고 사용하는 컴포넌트입니다. 리액트에서 인풋의 값을 제어하는 경우 리액트에서 지정한 값과 실제 input value 값이 항상 같습니다. 이 경우 값을 예측하기 편하고, 쉽게 바꿀 수 있다
는 장점이 있습니다.
비제어 컴포넌트는 input 태그의 value 속성을 리액트에서 지정하지 않고 사용하는 컴포넌트입니다.
그렇다면 어떤 컴포넌트를 쓰는게 더 좋을까요?
만약 둘 다 사용 가능하다면 제어 컴포넌트를 사용하는 게 좋습니다. 그러나 파일을 선택하는 경우에는 반드시 비제어 컴포넌트로 만들어야합니다.
그렇다면 다시 파일 input 이야기로 돌아와서 파일 초기화를 하는 법도 정리해보겠습니다.
const FileInput = () => {
const [value, setValue] = useState(null);
const inputRef = useRef();
const handleChange = (e) => {
const nextValue = e.target.files[0];
setValue(nextValue);
};
const handleClear = () => {
if (!inputRef.current) return;
inputRef.current.value = null;
setValue(null);
};
return (
<div>
<input type="file" ref={inputRef} onChange={handleChange} />
<button onClick={handleClear}>X</button>
</div>
);
}
export default FileInput;
이 경우 X 버튼을 누르면 handleClear가 실행됩니다.
이 때, inputRef.current가 있는지 확인하고 DOM 노드의 value값을 빈 문자열로 변경합니다.