form elements: <input>, <textarea>, <select> ...
제어 컴포넌트
HTML form elements는 자체적인 내부 상태를 가지고 있으며 유저의 입력에 기반하여 업데이트합니다. 이에 반해 React에서는 변화하는 상태는 전형적으로 컴포넌트의 state 에 저장되어 setState 또는 useState hook에 의하여만 변경됩니다.
제어 컴포넌트란 이 두가지 특성을 결합하여 React의 상태를 "single source of truth"로 만든 것입니다. 그리하여 form을 렌더하는 컴포넌트는 이후의 유저 입력에 따라 무엇이 발생할지 제어할 수 있습니다. 이와 같이 form element의 value가 React 에 의해 제어되는 form element를 제어 컴포넌트라고 부릅니다.
This flow kind of ‘pushes’ the value changes to the form component, so the Form component always has the current value of the input, without needing to ask for it explicitly.
so the Form component always has the current value of the input, without needing to ask for it explicitly.
const Input = () => {
const [value, setValue] = useState("");
const handleChange = (event) => {
setValue(event.target.value);
}
return <input type="text" value={value} onChange={handleChange}/>
}
비제어 컴포넌트란?
const Form = () => {
const ref = useRef(null);
const handleSubmit = (event) => {
event.preventDefault();
console.log(ref.current.value);
}
return (
<form onSubmit={handleSubmit}>
<input type="text" ref={ref}/>
</form>
);
}
각각의 장단점은?
제어 컴포넌트는 state가 변경될 때마다 real-dom에 접근하여 render를 trigger하므로, DOM 조작 메서드가 빈번하게 실행되나, DOM 조작 메서드의 실행 빈도가 성능에 치명적인 수준이 아니므로, Single Source of Truth 원칙을 따름으로써 얻을 수 있는 개발상의 이점(디버깅의 편리 등)이 크다고 생각된다.
단점으로는, html element의 자체 state를 모두 useState를 활용하여 선언하고, 이를 함수를 통하여 제어하여야 하므로, 코드량이 많아지고 로직이 복잡해질 수 있다는 것이 단점이다.
비제어 컴포넌트의 장점은 React Code와 non-react Code가 섞여 있을 경우, 통합하기가 쉽다는 점이다. 또한, 제어 컴포넌트에 비해 코드량이 적어 쉽고 빠르게 (그러나 지저분하게) 개발할 수 있다.
개인적인 선호는?
왜 focus는 비제어 방식으로만 컨트롤 할 수 있는가?
비제어 컴포넌트는 반드시 ref를 사용하여야만 하는가?
https://reactjs.org/docs/forms.html
https://reactjs.org/docs/uncontrolled-components.html
https://goshakkk.name/controlled-vs-uncontrolled-inputs-react/
ref 란?
ref는 DOM 노드 또는 React Elements(instance)에 접근하기 위한 방법을 제공한다.
일반적인 React dataflow에서 부모 컴포넌트는 props를 통해서만 자식과 상호작용한다. 자식을 변경하기 위해서는 새로운 props를 넘겨주어 re-render한다. 이러한 전형적인 dataflow가 아닌 특별한 경우에 명령적으로 자식을 변경할 수 있는 수단으로서 React는 ref를 제공한다.
props를 넘겨서 할 수 있다면, ref를 사용하지 말라. 그럼에도 ref를 사용해야 다는 생각이 들면 상태 끌어올리기를 통해서 상태를 더 높은 계층에 둔다면 해결될 수도 있다.
ref는 React.createRef 로 생성하여 ref attribute에 넘겨준다. 그러면 리액트 노드에 대한 레퍼런스는 ref.current 로 접근가능합니다. 리액트 노드가 HTML Element인 경우, 이는 DOM 객체입니다. 클래스 컴포넌트인 경우, 마운트 된 클래스 컴포넌트의 인스턴스입니다. 함수 컴포넌트의 경우에는, 인스턴스가 없으므로 ref attribute를 사용할 수 없습니다. → forwardRef (HOC) 필요 다만, 함수 컴포넌트 내에서 다른 HTML Element 또는 클래스 컴포넌트에 ref를 사용할 수는 있습니다.
React.forwardRef accepts a rendering function as an argument. React will call this function with props and ref as two arguments. This function should return a React node.
callbackRef 는?
createRef 를 통해 생성한 ref attribute를 넘겨주는 것 대신에 함수를 ref attribute에 넘겨줄 수 있습니다. 이 함수는 React component instance 또는 HTML DOM element를 인자로 넘겨받습니다. 따라서 해당 인자에 접근하여 DOM조작을 수행할 수도 있으며 closure에 저장할 수도 있습니다.
createRef 와 useRef의 차이는?
this.inputRef = React.createRef();
const refContainer = useRef(initialValue);
useRef returns a mutable ref object whose .current property is initialized to the passed argument (initialValue). The returned object will persist for the full lifetime of the component.
This works because useRef() creates a plain JavaScript object. The only difference between useRef() and creating a {current: ...} object yourself is that useRef will give you the same ref object on every render.
Well, the difference is that createRef will return a new ref on every render while useRef will return the same ref each time.
instance가 없는 함수 컴포넌트에서 useRef는 값을 persist 하는 용도로도 활용될 수 있다. useState hook은 re-render를 trigger하지만, 그럴 필요가 없는 값들은 useRef에 넣을 수 있다.
const usePrevious = (value) => {
const ref = React.useRef()
React.useEffect(() => {
ref.current = value
}, [value])
return ref.current
}