리액트(React) 이해 기초 2 - Conditional Rendering
July 07, 2017
리액트로 컴포넌트를 만들다 보면, 빈번하게 사용 되는 패턴 중에 하나는 조건에 따라 다른 컴포넌트를 렌더링 하는 것이다. 예를 들어, 데이터가 로딩 중 일 때, 데이터가 없을 때 등등 다양한 상황에서 조건식에 따라 다른 컴포넌트를 렌더링 해야 할 때가 있다. 기본적인 자바스크립트에서 제공하는 조건문(if/else, switch/case 등등)을 사용 하여 렌더링 하는 것 뿐만 아니라 IIFE, HOC, 객체(object) 등을 사용 하여 조건에 맞는 컴포넌트를 렌더링을 할 수도 있다. 이런 다양한 방법에 대해 알아보자.
자바스크립트 조건문과 연산자를 사용한 방법
if / else
가장 기본적인 방법이다. 컴포넌트 prop 값에 따라 리턴하는 리액트 컴포넌트를 분기 처리한다.
const ConditionalComponent = ({ loading, data }) => {
if (loading) {
return <LoadingComponent />;
}
return <h1>{data}</h1>;
};
switch / case
조건이 다양하다면 switch / case를 활용 할 수도 있다.
const Dialog({ type, ...rest }) => {
switch(type) {
case 'share':
return <ShareDialog {...rest} />;
case 'edit':
return <EditDialog {...rest} />;
case 'delete':
return <DeleteDialog {...rest} />;
default:
return null;
}
};
&&, || (Logical AND, Logical OR)
논리 연산자(Logical Operator)를 사용하여 조건부 처리 방법.
const LogicalAnd = ({ username }) => {
return <h1>{username && <span>username</span>}</h1>;
};
const LogicalOr = ({ username }) => {
return <h1>{username || 'no username'}</h1>;
};
condition ? expr1 : expr2 (Ternary Operator)
삼항 연산자를 사용하여 분기처리 하는 방법.
const TernaryComponent = ({ loading, data }) => {
return {loading ? <LoadingComponent /> : <h1>data</h1>};
};
HOC(Higher Order Component)를 이용한 조건부 렌더링
조건부 렌더링을 담당하는 HOC를 만들어 실제 컴포넌트에서는 조건을 신경쓰지 않고(관심사 분리) 렌더링을 하도록 하면, 컴포넌트 자체의 목적도 더 분명하고 재사용성도 증가 시킬 수 있다.
const Success = () => {
return <h1>good</h1>;
};
const False = () => <h1>condition fail</h1>;
const withCondition = (condition, FalsyComponent) => {
if (condition()) {
return WrappedComponent => WrappedComponent;
}
return () => FalsyComponent;
};
const SuccessWithCondition = withCondition(() => false, False)(Success);
즉시 실행 함수 IIFE(Immediately Invoked Function Expression)를 이용한 방법
IIFE를 활용해서 분기 처리를 할 수도 있다.
const sampleComponent = () => {
return (
<div>
{(() => {
if (flag && flag2 && !flag3) {
if (flag4) {
return <p>Blah</p>;
} else if (flag5) {
return <p>Meh</p>;
} else {
return <p>Herp</p>;
}
} else {
return <p>Derp</p>;
}
})()}
</div>
);
};
자바스크립트 객체를 활용한 방법
prop의 값이 여러개라면 if / else나 switch / case statement를 사용하기 보다는 객체(object)를 하나 만들어 key(prop value) / value(component)로 만들어 사용하면 코드도 훨씬 깔끔하고 가독성을 높일 수 있다.
import {
SHARE_DIALOG,
CONFIRM_EDIT_DIALOG,
CONFIRM_DELETE_DIALOG,
} from './constants/ui';
import ShareDialog from './ShareDialog';
import ConfirmEditDialog from './ConfirmEditDialog';
import ConfirmDeleteDialog from './ConfirmDeleteDialog';
const DIALOG_COMPONENTS_MAP = {
[SHARE_DIALOG]: ShareDialog,
[CONFIRM_EDIT_DIALOG]: ConfirmEditDialog,
[CONFIRM_DELETE_DIALOG]: ConfirmDeleteDialog,
};
const GlobalDialog = ({ open, onClose, dialogType, dialogProps }) => {
if (!dialogType || !DIALOG_COMPONENTS_MAP[dialogType]) {
return null;
}
const SpecificDialog = DIALOG_COMPONENTS_MAP[dialogType];
return <SpecificDialog {...dialogProps} open={open} onClose={onClose} />;
};
외부 라이브러리를 활용하는 방법
recompose의 branch HOC를 활용한 방법
리액트 유틸리티 라이브러리인 recompose의 branch를 사용하여 분기 처리를 해주는 HOC 함수를 사용 할 수도 있다.
const spinnerWhileLoading = isLoading =>
branch(
isLoading, // boolean(true or false) 값을 리턴하는 함수
renderComponent(Spinner) // 조건식이 true일 때 렌더링
);
const enhance = spinnerWhileLoading(
props => !(props.title && props.author && props.content)
);
// 조건식이 false (isLoading === false)일 때 렌더링
const Post = enhance(({ title, author, content }) => (
<article>
<h1>{title}</h1>
<h2>By {author.name}</h2>
<div>{content}</div>
</article>
));
개인적으로 선호하는 방식은 아니라서 여기서 샘플소스를 다루진 않겠지만, https://github.com/AlexGilleran/jsx-control-statements나 https://github.com/romac/react-if 등의 외부 라이브러리를 사용하는 방법도 있으니 참고하자.
리액트에서 조건에 따라 다른 컴포넌트를 렌더링 하는 방법에 대해서 알아봤다. 사실, 리액트에서 제공하는 기능이 아닌 자바스크립트에 내장되어 있는 기능/특징을 활용한 방법이라서 특별해 보이진 않을 것이다. 보통 자바스크립트 템플릿 엔진의 경우 자체적으로 조건부 렌더링을 할 수 있는 특수한 문법을 제공하는 게 일반적이지만 리액트의 경우 자바스크립트의 순수 기능을 적극 활용 할 수 있도록 설계되어 있다. 이것이 처음 리액트를 접하는 분들에겐 헷갈릴 수 있는 부분이기도 하니 이점을 염두하고 리액트 컴포넌트 개발을 하도록 하자.