본문 바로가기
Language/React

[React 공식 Doc 가이드 #8] Conditional Rendering

by ocwokocw 2021. 2. 11.

- 이 글은 React 공식 홈페이지 Docs v16.8.3 (https://reactjs.org/docs) 에 기반하여 작성한 글입니다.

- Conditional Rendering

React 에서 Rendering 하고 싶은 element 들을 캡슐화해서 Component 단위로 만들 수 있다. 그리고 이런 Component 들을 state 상태에 따라서 조건부 렌더링을 할 수 있다.

 

React 에서 조건부 렌더링은 Javascript 에서 분기문과 같은 방법으로 동작한다. if 문이나 조건부 문법을 사용해서 elements 를 만들면 React 는 이 element 에 맞춰서 UI를 갱신한다.

 

아래 2개의 component 가 있다고 가정하자.

2개의 Component 를 화면에 이상없이 출력하였다. login 상태에서는 환영문구를 보여주고, login을 하지 않았다면 회원가입(sign up) 하라는 사이트를 많이 보았을 것이다. 유저 login 여부에 따라 둘 중 1문구만을 보여주는 Component 를 추가해보자.

Greeting component 에 전달된 props 의 isLoggedIn attribute 값에 따라서 문구가 바뀌는 것을 확인할 수 있다.


- element variables

element 를 변수에 저장할 수도 있다. 물론 app 이 동작하는데에는 차이가 없지만 Component 를 부분적으로 rendering 할 때 도움이 된다.

 

Login 여부에 따른 환영인사에 이어서 button component 도 만들어 보자.

여태까지 만든 Component 들을 기반으로 상태값에 의존하는(stateful 한) LoginControl component 를 만들어보자.

LoginControl component 는 상태값에 따라서 <Greeting /> component 와 함께 <LoginButton /> 이나 <LogoutButton /> 을 rendering 할 것이다.

 

LoginControl 을 만들어 보자! 원래 귀찮음을 이겨내지 못하고 복붙해서 돌려보고 끝내는 사악한 유저들을 위해 캡처를 올리나 코드가 너무 긴 관계로 하이라이트로 대체

 

function UserGreeting(props){
	return (
		<h1>Welcome back!</h1>
	);
}

function GuestGreeting(props){
	return (
		<h1>Please sign up.</h1>
	);
}

function Greeting(props){
	const isLoggedIn = props.isLoggedIn;
	
	if(isLoggedIn){
		return (<UserGreeting />);
	}
	
	return (
		<GuestGreeting />
	);
}

function LogInButton(props){
	return (
		<button onClick={props.onClick}>
			Login
		</button>
	);
}

function LogOutButton(props){
	return (
		<button onClick={props.onClick}>
			Logout
		</button>
	);
}

class LoginControl extends React.Component{
	
	constructor(props){
		super(props);
		
		this.logIn = this.logIn.bind(this);
		this.logOut = this.logOut.bind(this);
		
		this.state = {
			isLoggedIn : false
		};
		
	}
	
	logIn() {
		console.log("call logIn");
		this.setState({isLoggedIn : !this.state.isLoggedIn});
	}
	
	logOut() {
		console.log("call logOut");
		this.setState({isLoggedIn : !this.state.isLoggedIn});
	}
	
	render() {
		
		const isLoggedIn = this.state.isLoggedIn;
		let button;
		
		if(isLoggedIn){
			button = <LogOutButton onClick={this.logOut}/>;
		}
		else{
			button = <LogInButton onClick={this.logIn}/>;
		}
		
		return (
			<div>
				<Greeting isLoggedIn={this.state.isLoggedIn} />
				{button}
			</div>
		);
		
	}
	
}

ReactDOM.render(
	<LoginControl />,
	document.getElementById('root')
)

button 에 component 를 할당하는 if 문 부분을 보자. 가독성도 나쁘지 않고 꽤 괜찮아 보인다. React 는 위처럼 변수에 component 할당을 할 수도 있고, 위와 똑같은 기능을 JSX 안에서 inline 스타일로도 구현할 수 있다.


- Inline If with Logical && Operator

JSX 의 {} 에서 expression 을 사용했던 기억을 떠올려 보자. javascript expression 을 사용할 수 있었다. 그렇다면 눈치 빠른 사람들은 논리연산자에서 && 도 사용할 수 있을 것이라고 생각할 것이다.

 

{} 안에서 inline 방식으로 아까 코드를 변경해보자.

component 를 할당하기 위한 button 변수가 없어지고 3항 연산자로 대체 했다. 

 

다른 얘제도 한번 적용해보자. 메일이나 메신저 같은 것을 보면 읽지 않은 건들에 대해 counting 을 해서 표시해주는 기능을 많이 보았을 것이다. 메일이라고 가정하고 inline 문법을 이용해서 구현해보자.

Message 가 있을 때에는 건수가 표시되고 없을 때에는 환영문구만 표시된다. 회사원들에게 끔찍한 Re: Fw:는 덤

message의 길이가 0 이상이면 true && element 여서 element 를 출력하지만 message 가 비어있어서 길이가 0이면 false && element 일 때 출력하지 않는것을 확인할 수 있다.

 

JSX 안에서 {} 에 대한 평가식이 false 일 때 rendering 을 하지 않으며, element 는 boolean 평가식에서 true 로 취급됨을 알 수 있는 대목이다.


- Inline If-Else with Conditional Operator

Javascript 의 조건부 연산자에 3항 연산자가 있다. (condition ? true : false)

 

그럴의도는 아니였는데 login logout 버튼을 {} 안에서 inline 방식을 소개할 때 3항 연산자를 써버렸다. 3항 연산자를 쓸 때 길이가 긴 element 를 표현하고자 한다면 확실하게 구분하게 해주기 위해 () 를 추가해줄 수도 있겠다.

{} 에는 Javascript 표현식을 모두 쓸 수 있으므로,(굳이 기억할 필요 없다. JSX 가 Javascript XML 약자임을 떠올려 보면 알 것이다.) 적절하게 잘 활용하면 될 것이다.

 

1가지만 기억하면 된다. 표현하고자 한 element 가 길어질 경우 () 로 구분할 수 있어서 문법적으로는 이상이 없지만 동료개발자들의 눈을 보호해주기 위해 component 단위로 따로 뺄 수 있지 않을까 하는 의심을 언제나 가지자. 리팩토링 개념을 알고 있는 사람이라면 언제나 긴 코드에는 bad smell 이 난다는 사실을 기억하면 될 것 같다.


- Preventing Component from Rendering

Rendering을 하기 위해서 component 를 사용하는건데 rendering 을 방지하는 component 라니 이게 무슨소리 인가? 한국말은 끝까지 들어보라더니 영어도 별반 다를 건 없다. 드문 경우이긴 하겠지만 다음과 같은 경우가 필요할 수도 있다.

 

자기 자신은 Rendering 하지 않지만, 다른 Component 에 의해서 Rendering 되는 component 라면 위의 소제목이 이해가 갈 것이다.

 

return 문에 무조건 element 를 할당하면 해당 Component 를 사용할 때 그려질텐데 어떻게 가능하단 말인가. 눈치가 빠른 사람이라면 이미 눈치 챘겠지만 return 문에 null 을 할당하면 된다.

 

사용자에게 위험사항이 있을 때만 출력해주는 <Warning Banner /> component 를 만들어 보자. prop값이 false 이면 component 가 rendering 이 되지 않게 하자.

function WarningBanner(props){
	if(!props.warn){
		return null;
	}
	
	return (
		<div className="warning">
			Warning!
		</div>
	);
}

class Page extends React.Component{
	
	constructor(props) {
		super(props);
		this.state = {
			showWarning: true
		};
		this.handleToggleClick = this.handleToggleClick.bind(this);
	}
	
	handleToggleClick() {
		this.setState({showWarning: !this.state.showWarning});
	}
	
	render() {
		return (
			<div>
				<WarningBanner warn={this.state.showWarning} />
				<button onClick={this.handleToggleClick}>
					{this.state.showWarning ? 'Hide' : 'Show'}
				</button>
			</div>
		);
	}
}

ReactDOM.render(
	<Page />,
	document.getElementById('root')
)

똑똑한 코더라면 render 에서 null을 return 하면 lifecycle method 에 영향을 주는 것이 아닌가라고 생각할 수도 있다. render method 에서 null 을 return 하더라도 component 의 lifecycle method 에 영향을 주지 않는다.

 

componentDidUpdate 는 여전히 호출 될 것이다. 라면서 친절하게 설명해주지만 검증을 해보자.

구라는 아닌가보다.

댓글