Conditional Rendering for React
리액트에서는 조건부 렌더링을 사용하기 위한 방법으로 아래와 같은 방식을 제시하고 있다.
요약하자면,
컴포넌트 자체 처리
<Component show={condition} />
inline if
{condition && <Component />}
삼항연산자
{condition ? <Component /> : null or other component}
변수로 담아두기
let component;
if (condition) {component = <Component />}
else {component = null}{component}
함수로 담아두기
renderComponent() {
if (condition) {
return <Component />
}
return null
}{this.renderComponent()}
하지만 이와 다르게 조건문을 처리해보고자 하는 시도들이 여럿 있었다. 이 글에서는 이런 방법들을 한 번 소개해보고자 한다.
<If cond={}>
// Example Code from react-if
class Beer extends React.Component { render() {
return (
<div>
<If condition={ this.props.age >= 16 }>
<Then>Have a beer, {this.props.name}!</Then>
<Else>{() => // will only be evaluated if the condition fails.
<span>Sorry, {this.props.name}, you are not old enough.</span>
}</Else>
</If>
</div>
);
}}
혹은 컴포넌트가 아닌 Babel에 처리를 맡겨버리는 방식도 있다.
이런 방법을 찾아보던 중, 나에게는 굳이 If Then Else 문법까지 필요할 것 같지 않아서 직접 구현해보기로 했다. 생각보다 간단한 코드다.
여기에서 If의 children을 function으로 반-강제하는 것은, function이 아닌 컴포넌트의 형태로 자식 컴포넌트를 표현할 경우, 조건의 참 여부와 관계없이 자식 컴포넌트를 만들어내는 코드가 실행되어 평가되기 때문이다. 쉬운 코드로 풀어 쓰자면,
// If의 children이 컴포넌트일 경우
let trueResult = React.createElement(Component) // condition에 상관없이 실행됨
let falseResult = nullif (condition) {
return trueResult
} else {
return falseResult
}// If의 children이 함수일 경우
if (condition) {
return getTrueResult() // condition == true일 때에만 실행됨
} else {
return null
}
한편, 이렇게 <If> 컴포넌트를 만들어 사용하는 일이 굉장히 React에 적합하다고 생각해 신나게 사용하던 차에, 이 컴포넌트의 몇 가지 단점을 발견했다.
- If 역시 컴포넌트다. 컴포넌트는 단 하나의 Root 엘리먼트를 가져야 한다. 즉, 아래와 같은 형식은 If로 대체할 수 없다.
{condition ? [<Comp1 key={1} />, <Comp2 key={2} />] : null}
- If의 children이 function이기 때문에, children에 대한 validation이 어렵다.
- CSSTransitionGroup과 같이 컴포넌트 렌더링에 관여하는 역할을 가진 라이브러리와의 궁합이 잘 맞지 않는다.
더 많은 논의는 아래 링크에서 확인할 수 있다.
결론
하지만 나는 계속해서 <If>를 사용하기로 했다. 일단, 기존에 제시된 방법들이 어떻게 보아도 JSX 속에서 깔끔해보이지 않았던 것이 첫 번째 이유이다. 다른 이유로는, <If>를 사용하면 JSX에서 {} 내부에 작성되는 대부분의 코드를 텍스트 노드로 제한할 수 있다. 하나의 컨벤션이 될 수 있는 것이다.
하지만 단서 조항을 하나쯤 달아두기로 했다. 경우의 수가 4가지 이상 되어 switch문이 더 간편한 경우나, <If> 내부에 10줄 이상의 코드가 필요할 경우 {this.renderSomething()}으로 분리하고 이 분리된 함수 내부에서 분기를 처리하는 방식을 사용하기로 했다.