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} /> } } }
- logProps의 문제점
-
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} /> }) }