[React 시작하기] 04. 리액트 실전 활용법 - HOC, Controlled Component
HOC (Higher Order Component)
고차 컴포넌트 – React
A JavaScript library for building user interfaces
ko.reactjs.org
컴포넌트를 가공하여 새로운 컴포넌트로 내보내는 React 패턴. (API가 아님)
const EnhancedComponent = higherOrderComponent(WrappedComponent);
즉, <컴포넌트>를 인자로 받아 <새로운 컴포넌트>를 리턴하는 함수이다.
최근에는 비슷한 역할을 하는 Hook이 출시됐다.
HOC 종류
Connect | React Redux (react-redux.js.org)
Connect | React Redux
API > connect: a Higher-Order Component to interact with Redux
react-redux.js.org
Fragment Container | Relay
A Fragment Container is a higher-order component that allows components to specify their data requirements. A container does not directly fetch data, but instead declares a specification of the data needed for rendering, and then Relay will guarantee that
relay.dev
1-5. withRouter · GitBook (vlpt.us)
1-5. withRouter · GitBook
05. withRouter 라우트가 아닌 컴포넌트에서 라우터에서 사용하는 객체 - location, match, history 를 사용하려면, withRouter 라는 HoC 를 사용해야합니다. src/components/ShowPageInfo.js import React from 'react'; import { wi
react-router.vlpt.us
보통 with가 붙은 함수는 HOC인 경우가 많다.
withRouter
라우트가 아닌 컴포넌트에서 라우터를 사용하는 객체 - location, match, history를 사용할 때 필요하다.
withRouter를 사용하지 않으면 props가 들어가지 않았는데, 사용하면 LoginButton에 들어갈 props를 넣어준다.
HOC 사용법
횡단 관심사(Cross-Cutting Concerns)에 고차 컴포넌트(HOC) 사용하기
횡단 관심사란 여러 컴포넌트에서 공통적으로 가지고 있는 기능을 의미한다.
예를 들면, A 컴포넌트는 망치를 구현하고, B 컴포넌트는 스패너를 구현할 때 두 컴포넌트 모두 '자루'를 구현해야 한다.
기존에는 이러한 공통 요소를 mixin으로 구현했으나 React에서는 mixin이 많은 문제를 일으켜 사용을 권장하지 않는다.
유해한 것으로 간주되는 믹스 인 - 반응 블로그 (reactjs.org)
Mixins Considered Harmful – React Blog
“How do I share the code between several components?” is one of the first questions that people ask when they learn React. Our answer has always been to use component composition for code reuse. You can define a component and use it in several other co
ko.reactjs.org
따라서 요즘에는 mixin 대신에 HOC를 이용해 공통 기능을 묶어내는 방법을 사용한다.
자세한 내용은 상단의 React 문서를 참고한다.
원본 컴포넌트를 변경하지 말고 조합(Composition) 하라.
고차 컴포넌트(HOC) 내부에서 컴포넌트의 프로토타입을 수정하면 안된다.
그 이유는 다음과 같다.
1. 수정된 프로토타입은 더 이상 HOC와 별도로 재사용될 수 없다.
2. 한번 원본을 수정한 고차 컴포넌트를 또 다른 고차 컴포넌트의 재료로 사용하면 첫번째 HOC의 기능이 무시된다.
따라서 HOC는 변경대신에 입력 컴포넌트를 컨테이너 구성 요소로 감싸서 조합하는 방식을 사용해야 한다.
컨벤션: 래핑된 컴포넌트를 통해 관련없는 Props 전달하기
고차 컴포넌트는 컴포넌트에 기능을 추가한다.
하지만 그렇다고 해서 래핑된 컴포넌트의 정의를 크게 변경해서는 안된다.
고차 컴포넌트에서 반환된 컴포넌트는 래핑된 컴포넌트와 비슷한 인터페이스가 있어야 한다.
예를 들어서, withRouter()는 매개변수로 들어오는 컴포넌트에 props를 자동으로 구해서 넣어준다.
그렇게 만든 HOC 컴포넌트를 다른 곳에서 사용하게 될 때, 우리가 직접적으로 넣어주는 Props가 또 존재할 것이다.
이렇게 우리가 직접 넣은 props와 withRouter가 자동으로 생성한 props는 서로 영향을 주게 되면 좋지 않다.
우리가 넣어준 props가 바로 Unrealated Props(관련 없는 Props)이고 이러한 props는 따로 전달하는것이 좋다는 것이다.
컨벤션: 조합 가능성(Composability) 끌어올리기
고차 컴포넌트는 여러가지 방법으로 작성할 수 있다.
단일 인수로 래핑된 컴포넌트를 받을 수도 있지만, 일반적으로 추가 인수를 허용한다.
컨벤션: 간단한 디버깅을 위한 디스플레이 네임 작성
HOC로 만든 구성 요소도 React Developer Tools에 표시된다.
이 때 HOC의 결과임을 알리는 디스플레이 네임을 작성하면 추후 디버깅시에 효과적이다.
예를 들어, HOC이름이 withRouter 이고 HOC 내부 컴포넌트 이름이 LoginButton 인 경우
디스플레이 네임은 withRouter(LoginButton) 을 사용한다.
주의사항
render 메서드 안에서 고차 컴포넌트를 사용하면 안된다.
HOC는 컴포넌트를 가지고 새로운 컴포넌트를 만든다는 의미인데
render 메서드 안에 넣으면 render 될 때마다 새로운 컴포넌트를 만들게 되므로 비효율적이다.
정적 메서드(Static Methods)는 반드시 따로 복사해야한다.
HOC로 만든 새 컴포넌트는 기존 컴포넌트의 정적 메서드를 가지고 있지 않으므로
메서드 반환 전에 반드시 컨테이너에 복사해주어야 한다.
HOC인 enhance로 새로운 컴포넌트를 만들면 staticMethod가 넘어오지 않는다.
따라서 위와 같이 enhance의 정의에 staticMethod를 복사하는 기능을 추가해서 사용했어야 하는데 이게 귀찮음
hoistNonReactStatic 이라는 패키지를 통해 staticMethod를 한번에 옮겨주는 방식이 등장했다.
GitHub - mridgway/hoist-non-react-statics: Copies non-react specific statics from a child component to a parent component
Copies non-react specific statics from a child component to a parent component - GitHub - mridgway/hoist-non-react-statics: Copies non-react specific statics from a child component to a parent comp...
github.com
따로 이렇게 someFunction이라는 staticmethod를 따로 빼서 사용할 수도 있다.
ref는 전달되지 않는다.
고차 컴포넌트는 모든 props를 래핑된 컴포넌트에 전달하는 것이 원칙이나, refs는 작동하지 않는다.
그 이유는, React에서 refs가 key처럼 특별하게 취급되기 때문이다.
따라서 ref는 React.forwardRef API를 통해 전달해야 한다.
Controlled Component , Uncontrolled Component
input, select, textarea 등의 엘리먼트들은 모두 '상태'를 가지고 있다.
이러한 '상태'를 누가 관리하느냐에 따라 Controlled Component와 Uncontrolled Component가 나뉜다.
- Controlled Component: 엘리먼트를 가지고 있는 컴포넌트가 관리
- Uncontrolled Component: 엘리먼트의 상태를 관리하지 않고, 엘리먼트의 참조만 컴포넌트가 소유
폼 – React
A JavaScript library for building user interfaces
ko.reactjs.org
Controlled Component
즉, Controlled Component는 input 태그의 내용과 컴포넌트의 state가 같은 상태를 유지하는 것을 의미한다.
예시
위와 같이 input에 state.value를 바인딩 한 뒤 onChange를 통해 _change 메소드가 실행되게 만들었다.
_change 메소드는 value에 이벤트 객체로 전달된 입력값을 저장한다.
setState 메소드가 실행됐으므로 컴포넌트는 다시 렌더링이 되며, input 태그의 value는 state.value와 동일하게 변경된다.
Uncontrolled Components
비제어 컴포넌트 – React (reactjs.org)
비제어 컴포넌트 – React
A JavaScript library for building user interfaces
ko.reactjs.org
비제어 컴포넌트는 Ref 라는 중요한 API를 사용하게 된다.
위와 같이 구성하면 실제 DOM을 조작하여 데이터를 얻어올 수 있다.
하지만 React는 가상 DOM을 구현하는 방식을 지향하므로 실제 DOM을 조작하는 방식은 좋지 않다.
따라서 이 때 필요한 것이 ref 개념이다.
ref
다음과 같은 형태로 사용한다.
변수명 = React.createRef();
DOM에서 특정 요소를 선택한 뒤 변수에 저장하여 사용하는 방식이다.
위의 코드를 해석해보면, 버튼을 클릭할 때마다 click 메소드가 실행되고 click 메소드에서는 _inputRef를 참조한다.
_inputRef는 참조될 때마다 createRef() 메소드를 통해 값을 불러온다.
즉, 버튼을 누를 때마다 그 때의 input 값이 _inputRef 에 저장되게 된다.
정리
Controlled 컴포넌트는 실시간 작업에 용이하고
Uncontrolled 컴포넌트는 비실시간 작업에 용이하다.
예를 들어, 입력되는 값을 감지하여 틀린 형태일 경우 빨간색, 맞는 형태일 경우 초록색을 나타내는 기능을 구현하려면
Controlled 컴포넌트를 이용하는 것이 유용하고
어떤 버튼을 눌러 다크모드/ 화이트모드 전환 등의 기능을 구현할 때는 Uncontrolled 컴포넌트를 사용하는 것이 유용하다.