[Day 44] React Advance(HOC), Page Transition, React Router
React Advance
고차 컴포넌트 (Higher-Order Component)
HOC는 컴포넌트를 인자로 받아서 새로운 컴포넌트를 반환하는 '함수'
이다. —> (엘리먼트를 반환하지 않기 때문에 컴포넌트가 아니라 함수다.)
컴포넌트는 props를 받아서 UI(element)로 반환하지만, HOC는 컴포넌트를 다른 컴포넌트로 바꿔서 반환한다.
횡단 관심사(Cross-Cutting Concerns)를 위해 HOC 사용하기
(횡단 관심사?) 애플리케이션을 구현하는 데 있어서 여러 페이지에서 공통적으로 사용되어야 하는 부분이 있는데, 이를 횡단 관심사라고 한다.
(중요!) HOC는 입력받은 컴포넌트를 수정하지도, 상속받지도 않는다. —> 대신, 원래의 컴포넌트를 다른 컴포넌트로 감싸는 식으로 합성한다. (즉, HOC는 외부세계에 영향을 주지 않는 순수함수이다.)
(중요!) HOC에서 입력받은 컴포넌트에 메소드나 함수를 추가하는 등의 변경을 가하면 해당 컴포넌트를 사용하는 다른 곳에서 영향을 받는다. —> 대신, 새로운 컴포넌트를 만들어서 기능을 추가하고 입력받은 컴포넌트를 render()에서 그려주기만 하자.
(관례*) HOC와 무관한 props는 감싸진 컴포넌트에 넘기자. —> 원래 컴포넌트나, 업그레이드된 컴포넌트나 크게 다르지 않게 사용할 수 있어야 한다. 따라서, 컴포넌트의 사용법을 극단적으로 바꾸지 말자. —> HOC는 그 자신이 필요로하는 props 외에 나머지 props를 감싸진 컴포넌트에 그대로 넘겨야 한다.
(관례*) HOC를 사용해 합성할 때, 인자로 컴포넌트만 입력받는 것이 아니라 다른 기능을 위한 함수를 함께 받거나, 함수의 리턴값으로 함수를 호출하는 등의 기법들을 모두 이용해 합성해서 사용한다.
(단점..) 입력받은 컴포넌트를 감싸는 새로운 컴포넌트를 만드는 구조이기 때문에 컴포넌트의 계층구조가 깊어지고, 복잡해진다. —> 그리고, 개발자 도구에서 감싸는 컴포넌트의 이름을 확인하기 어렵다.
(관례*) 개발자 도구에서 감싸는 컴포넌트의 이름을 정확히 표시해주기 위해 displayName
을 사용할 수 있다.
(주의!) render() 메소드 안에서 HOC를 사용하지 말자. —> render() 메소드에서 HOC를 사용하면 매번 새로운 컴포넌트를 만들고, 새로운 컴포넌트를 만들 때마다 컴포넌트 타입이 바뀌기 때문에 상태를 매번 날려버리고 새로 그린다. —> 이러한 문제점을 해결하기 위해 HOC는 단 한번만 적용되게 만들어줘야 한다. (즉, 횡단 관심사를 export 하는 쪽에서 HOC를 둘러주면 된다.)
(주의!) Ref는 전달되지 않는다. —> Key
나 Ref
는 진짜 props가 아니라 특별취급 되기 때문에 props로 전달될 수 없기 때문이다. —> 이 때, Ref는 안쪽 컴포넌트가 아닌 감싸는 컴포넌트에 전달된다. —> 이런 문제를 해결하기 위해 React.forwardRef
API를 사용하거나, ref가 아닌 다른 이름(ex. innerRef
)를 이용해서 넘겨준다.
// HOC 선언 (ex. UserContext.js)
function withUser(WrappedComponent) {
function WithUser(props) {
return (
<Consumer>
{value => <WrappedComponent {...value} {...props} />}
</Consumer>
)
}
WithUser.displayName = 'WithUser(!!!)'
return WithUser
}
// HOC 사용
import {withUser} from '../contexts/UserContext'
class LoginForm extends Component {
...
}
export default withUser(LoginForm)
Page Transition
HTML5 History API
DOM의 window
객체는 history
객체를 통해 브라우저 히스토리에 접근할 수 있다.
브라우저에서는 페이지(링크) 이동에 따른 탐색 내용을 History Stack에 Push하는데, 뒤로가기 버튼을 누르면 Stack에서 현재 탐색 내용을 Pop 한다.
History Methods
앞으로 가기, 뒤로가기
// 앞으로 가기
window.history.back();
// 뒤로 가기
window.history.forward();
히스토리에서 특정 위치로 가기
// 한 페이지 뒤로
window.history.go(-1);
// 한 페이지 앞으로
window.history.go(1);
히스토리 스택의 페이지 구성을 알아내기
var numberOfEntries = window.history.length;
History Entry
HTML5에서 생긴 기능을 통해 페이지를 새로고침하지 않고도 직접 URL 주소를 바꿔줄 수 있다.
사용하가 히스토리 엔트리를 추가하거나 변경할 수 있는 메소드
-
history.pushState()
var stateObj = {foo : "bar"}; // 실제 'bar.html' 파일을 불러오는 것이 아니라, 주소 표시줄만 바꿔준다. history.pushState(stateObj, "page 2", "bar.html");
-
history.popState()
뒤로가기 버튼을 누를 때 문서에서
popstate
이벤트를 발생시킨다.
popState / pushState
Undo, Redo 기능과 마찬가지로 Stack을 2개를 둬야한다.
뒤로가기를 하면 stack1에서 pop 해서 stack2에 push해주고,
앞으로 가기를 하면 stack2에서 pop 해서 stack1에 push 해준다.
HashChange
(개념) 링크 이동에서 #(hash)를 이용해서 위치를 이동하는 기법.
주로 HTML 태그의 id와 연결해서 많이 쓴다.
JS에서 이벤트 리스너로도 사용할 수 있다.
SPA (Single Page Application)
(개념) HTML 페이지 하나만 가지고 모든 페이지를 다 보여주는 App
(단점..) 직접 주소표시줄(History)를 관리해줘야 한다.
React 에서의 Page Transition
pushState, popState 를 활용해서 History 상태 저장소와 React 상태 저장소를 연결시켜준다.
React의 상태가 변하면 화면이 다시 그려지는 성질을 이용한다.
React Router
현재는 React Router - version 4 (명령형에서 선언형으로 구조가 바뀌었다.)
쉽게 말하면 주소 표시줄의 상태를 바꿔주는 라이브러리라고 볼 수 있다.
(중요!) 무엇을 페이지로 볼 것인가? —> 페이지는 즐겨찾기가 가능한 하나의 단위. —> 사용자의 입장에서 어떤 것을 즐겨찾기를 할 것인지를 생각해서 페이지 단위를 설정해야 한다.
댓글남기기