Higher-Order Components
September 27, 2020
HOC란?
- 컴포넌트의 공통 기능을 관리하기 위해 사용되는 함수
- 컴포넌트를 입력으로 받아서 컴포넌트를 출력해주는 함수이다.
- HOC를 사용하여 코드의 중복을 줄일 수 있다.
HOC를 사용하여 코드의 중복을 줄일 수 있다.
- componentDidMount
- componentWillUnmount
- handleChange
class CommentList extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {
comments: DataSource.getComments()
};
}
componentDidMount() {
DataSource.addChangeListener(this.handleChange);
}
componentWillUnmount() {
DataSource.removeChangeListener(this.handleChange);
}
handleChange() {
this.setState({
comments: DataSource.getComments()
});
}
render() {
return (
<div>
{this.state.comments.map((comment) => (
<Comment comment={comment} key={comment.id} />
))}
</div>
);
}
}
class BlogPost extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {
blogPost: DataSource.getBlogPost(props.id)
};
}
componentDidMount() {
DataSource.addChangeListener(this.handleChange);
}
componentWillUnmount() {
DataSource.removeChangeListener(this.handleChange);
}
handleChange() {
this.setState({
blogPost: DataSource.getBlogPost(this.props.id)
});
}
render() {
return <TextBlock text={this.state.blogPost} />;
}
}
HOC 활용
function withSubscription(WrappedComponent, selectData) {
return class extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {
data: selectData(DataSource, props)
};
}
componentDidMount() {
DataSource.addChangeListener(this.handleChange);
}
componentWillUnmount() {
DataSource.removeChangeListener(this.handleChange);
}
handleChange() {
this.setState({
data: selectData(DataSource, this.props)
});
}
render() {
return <WrappedComponent data={this.state.data} {...this.props} />;
}
};
}
const CommentListWithSubscription = withSubscription(
CommentList,
(DataSource) => DataSource.getComments()
);
const BlogPostWithSubscription = withSubscription(
BlogPost,
(DataSource, props) => DataSource.getBlogPost(props.id)
);
주의사항
-
Don’t Mutate the Original Component
-
logProps의 문제점
- 다른 HOC의 input으로 입력되면 componentDidUpdate가 override된다
- 함수형 컴포넌트는 componentDidUpdate가 없어 작동하지 않는다.
function logProps(InputComponent) { InputComponent.prototype.componentDidUpdate = function(prevProps) { console.log('Current props: ', this.props); console.log('Previous props: ', prevProps); }; // The fact that we're returning the original input is a hint that it has // been mutated. return InputComponent; } // EnhancedComponent will log whenever props are received const EnhancedComponent = logProps(InputComponent);
- 해결책
function logProps(WrappedComponent) { return class extends React.Component { componentDidUpdate(prevProps) { console.log('Current props: ', this.props); console.log('Previous props: ', prevProps); } render() { // Wraps the input component in a container, without mutating it. Good! return <WrappedComponent {...this.props} />; } } }
-
-
HOC내에서 render 생명주기 사용하지 않기
- 매번 새로운 값이라고 판단(React’s diffing alogirithm 구분하지 못함)
render() { // A new version of EnhancedComponent is created on every render // EnhancedComponent1 !== EnhancedComponent2 const EnhancedComponent = enhance(MyComponent); // That causes the entire subtree to unmount/remount each time! return <EnhancedComponent />; }
-
Static Method 전달
function enhance(WrappedComponent) { class Enhance extends React.Component {/*...*/} // Must know exactly which method(s) to copy :( Enhance.staticMethod = WrappedComponent.staticMethod; return Enhance; } // hoist-not-react-static 이용 import hoistNonReactStatic from 'hoist-non-react-statics'; function enhance(WrappedComponent) { class Enhance extends React.Component {/*...*/} hoistNonReactStatic(Enhance, WrappedComponent); return Enhance; } // 정적메소드만 따로 export하여 사용하기 // Instead of... MyComponent.someFunction = someFunction; export default MyComponent; // ...export the method separately... export { someFunction }; // ...and in the consuming module, import both import MyComponent, { someFunction } from './MyComponent.js';
-
Ref는 전달
function logProps(Component) { class LogProps extends React.Component { render() { const {forwardedRef, ...rest} = this.props; return <Component ref={forwardedRef} {...rest} />; } } return React.forwardRef((props, ref) => { return <LogProps {...props} forwardedRef={ref} />; }); }