Level 2: react-payments 학습 로그

동동·2021년 4월 24일
0

0. PR 링크

1. 제어 컴포넌트 vs 비제어 컴포넌트

  • form elements: <input>, <textarea>, <select> ...

  • form elements의 자체 상태에 대한 제어권을 누가 가지고 있느냐에 따라 나뉜다.

    • 컴포넌트가 제어권을 가지고 있다?: 제어 컴포넌트
    • form elements가 제어권을 가지고 있다?: 비제어 컴포넌트

제어 컴포넌트란?

  • HTML form elements는 자체적인 내부 상태를 가지고 있으며 유저의 입력에 기반하여 업데이트합니다. 이에 반해 React에서는 변화하는 상태는 전형적으로 컴포넌트의 state 에 저장되어 setState 또는 useState hook에 의하여만 변경됩니다.
  • 제어 컴포넌트란 이 두가지 특성을 결합하여 React의 상태를 "single source of truth"로 만든 것입니다. 그리하여 form을 렌더하는 컴포넌트는 이후의 유저 입력에 따라 상태가 변화하게 되고, 이를 바탕으로 form element의 value가 변화합니다. 이와 같이 form element의 value가 React 에 의해 제어되는 컴포넌트를 제어 컴포넌트라고 부릅니다.
  • 이는 마치 컴포넌트에 value 를 push하는 것과 같습니다. 이로 인해 컴포넌트는 항상 input의 현재 value를 가지고 있게 됩니다.
        const Input = () => {
          const [value, setValue] = useState("");
        
          const handleChange = (event) => {
            setValue(event.target.value);
          }
        
          return <input type="text" value={value} onChange={handleChange}/>
        }

비제어 컴포넌트란?

  • 제어 컴포넌트의 반대로서, form data 가 DOM 그 자체에 의해 제어되는 컴포넌트입니다. 비제어 컴포넌트를 작성하기 위해서는 모든 상태 변화에 대한 이벤트 핸들러를 작성하는 대신, ref를 사용하여 DOM으로부터 form value를 가지고 올 수 있습니다.
  • 필요할 때 value를 DOM으로부터 pull해야 합니다(가지고 와야 합니다).
    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가 변경될 때마다 diff algorithm에 의해 render를 trigger하므로, DOM 조작 메서드가 빈번하게 실행되나, DOM 조작 메서드의 실행 빈도가 성능에 치명적인 수준이 아니므로, Single Source of Truth 원칙을 따름으로써 얻을 수 있는 개발상의 이점(디버깅의 편리 등)이 크다고 생각된다.
  • 단점으로는, html element의 자체 state를 모두 useState를 활용하여 선언하고, 이를 함수를 통하여 제어하여야 하므로, 코드량이 많아지고 로직이 복잡해질 수 있다는 것이 단점이다.
  • 비제어 컴포넌트의 장점은 React Code와 non-react Code가 섞여 있을 경우, 통합하기가 쉽다는 점이다. 또한, 제어 컴포넌트에 비해 코드량이 적어 쉽고 빠르게 (그러나 지저분하게) 개발할 수 있다.

개인적인 선호는?

  • 개인적인 선호는 제어 컴포넌트. Single Source of Truth라는 원칙을 따르는 것이 좋고, 로직이 복잡해질 경우 formik 등의 library를 활용하는 방법을 고려해볼 수 있겠다.

왜 focus는 비제어 방식으로만 컨트롤 할 수 있는가?

  • focus 되어 있다는 속성은 어떤 엘리먼트의 상태가 아니다. DOM 객체내에 유일하게 포커스 되어 있는 요소가 존재한다. 즉, DOM의 상태(?)라고 할 수 있으므로 컴포넌트 수준에서 관리할 수 있는 상태가 아니다.
    document.activeElement로 어떤 htmlElement가 active인지(focused 인지) 확인할 수 있으나 이는 read-only property 이다

비제어 컴포넌트는 반드시 ref를 사용하여야만 하는가?

  • react 에서 실제 HTMLElement에 접근할 수 있는 방법은 ref와 querySelector 가 있다. querySelector를 하는 경우, 컴포넌트화에 따른 이점을 누릴 수 없다. class, id, tag name을 매번 고려하며 할 것인가? 이를 위해, 리액트에서는 ref를 사용하여 실제 HTMLElement에 접근할 수 있다. React는 해당 컴포넌트가 마운트되고 난 후 ref.current 에 HTMLElement 의 DOM 객체를 담아준다.

https://reactjs.org/docs/forms.html

https://reactjs.org/docs/uncontrolled-components.html

https://goshakkk.name/controlled-vs-uncontrolled-inputs-react/

2. createRef vs useRef

ref 란?

  • ref는 DOM 노드 또는 React Elements(instance)에 접근하기 위하여 React에서 제공하는 방법입니다.

  • 일반적인 React dataflow에서 부모 컴포넌트는 props를 통해서만 자식과 상호작용한다. 자식을 변경하기 위해서는 새로운 props를 넘겨주는 방식입니다. 이러한 전형적인 dataflow가 아닌 특별한 경우에 명령적으로 자식을 변경할 수 있는 수단으로서 React는 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.

  • props를 넘겨서 할 수 있다면, ref를 사용하지 말라. 그럼에도 ref를 사용해야 다는 생각이 들면 상태 끌어올리기를 통해서 상태를 더 높은 계층에 둔다면 해결될 수도 있다.

callbackRef 란?

  • createRef 를 통해 생성한 ref attribute를 넘겨주는 것 대신에 함수를 ref attribute에 넘겨줄 수 있습니다. React는 컴포넌트가 마운트 되고 나면 callback ref를 React component instance 또는 HTML DOM element를 인자로 하여 실행하며, 언마운트 될 때에 null을 인자로 하여 실행합니다. 따라서 해당 인자에 접근하여 DOM조작을 수행할 수도 있으며 closure에 저장할 수도 있습니다.

createRef 와 useRef의 차이는?

this.inputRef = React.createRef();

const refContainer = useRef(initialValue);

createRef는 매 render시에 새로운 ref를 반환하는 반면, useRef는 매번 같은 ref를 반환합니다.

클래스 컴포넌트에서는 constructor 에서 createRef를 통해 ref를 생성하면 컴포넌트의 생애주기와 ref의 생애주기가 일치하지만, 함수 컴포넌트는 인스턴스화 되지 않고 한 번 실행후 값을 반환하고 종료되므로, 함수 컴포넌트에서 craeteRef를 계속 실행하면 새로운 ref 객체를 계속 생성하게 됩니다. 이는 불필요한

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;
};

https://reactjs.org/docs/refs-and-the-dom.html

https://reactjs.org/docs/forwarding-refs.html

https://reactjs.org/docs/react-api.html#reactforwardref

https://reactjs.org/docs/hooks-reference.html#useref

https://dev.to/dinhhuyams/introduction-to-useref-hook-3m7n#:~:text=Well%2C the difference is that,full lifetime of the component.

https://stackoverflow.com/questions/54620698/whats-the-difference-between-useref-and-createref

https://woowacourse.github.io/javable/post/2021-05-15-react-ref/

profile
작은 실패, 빠른 피드백, 다시 시도

0개의 댓글