리액트 기본 배우기 -조건부 렌더링에 대해-

1. 조건부 렌더링

 

조건에 따른 렌더링

 

어떠한 조건에 따라 렌더링이 달라지는 것

 

조건문의 결과 true아니면 false인데 이 결과에 따라 렌더링을 다르게 하는 것

 

function UserGreeting(props) {
    return <h1>다시 오셨군요!</h1>
}

function GuestGreeting(props) {
    return <h1>회원가입을 해주세요.</h1>
}

 

위 코드에서는 UserGreeting과 GuestGreeting 두개의 함수 컴포넌트가 있다

 

UserGreeting은 이미 회원인 사용자에게 보여줄 메시지를 출력하는 컴포넌트이고

 

GuestGreeting은 아직 가입하지 않은 게스트 사용자에게 보여줄 메시지를 출력하는 컴포넌트이다.

 

회원인지 아닌지에 따라 2개의 컴포넌트를 선택적으로 보여줘야할 것이다.

 

function Greeting(props) {
    const isLoggedIn = props.isLoggedIn;

    if(isLoggedIn) {
        return <UserGreeting />
    }
    return <GuestGreeting />
}

 

 

위에 나오는 Greeting 컴포넌트는 isLoggedIn이라는 변수 값이 true에 해당되는 값이면 UserGreeting,

 

그렇지 않으면 GuestGreeting 컴포넌트를 리턴한다

 

props로 들어오는 isLoggedIn의 값에 따라 화면에 출력되는 결과가 달라진다.

 

이렇게 조건에 따라 렌더링의 결과가 달라지도록 하는 것이 조건부 렌더링

 

 

2. truthy, falsy

 

자바스크립트에서 true는 아니지만 true로 여겨지는 값을 truthy

 

false는 아니지만 false로 여겨지는 값을 falsy

 

2-1) truthy

 

true

 

{}

 

[]

 

0이 아닌 number

 

공백 문자열이 아닌 문자열 "0", "false"

 

2-2) falsy

 

false

 

0, -0

 

0n(big int zero)

 

'',"",``(공백 문자열)

 

null

 

undefined

 

NaN

 

 

3. 엘리먼트 변수

 

렌더링해야 될 컴포넌트를 변수처럼 다루고 싶을 때 엘리먼트 변수 사용

 

리액트 앨리먼트를 변수처럼 다루는 방법

 

function LoginButton(props) {
    return (
        <button onClick={props.onClick}>
            로그인
        </button>
    )
}

function LogoutButton(props) {
    return (
        <button onClick={props.onClick}>
            로그아웃
        </button>
    )
}

 

위 코드에서 LoginButton, LogoutButton 두개의 컴포넌트가 있다.

 

각 컴포넌트는 이름 의미처럼 로그인 버튼과 로그아웃 버튼이다.

 

아래 LoginControl 컴포넌트에는 사용자의 로그인 여부에 따라 두개의 컴포넌트를 선택적으로 보여준다.

 

function LoginControl(props){
    const [isLoggedIn, setIsLoggedIn] = useState(false)

    const handleLoginClick = () => {
        setIsLoggedIn(true)
    }

    const handleLogoutClick = () => {
        setIsLoggedIn(false)
    }

    let button;
    if (isLoggedIn) {
        button = <LogoutButton onClick={handleLogoutClick}/>

    }else {
        button = <LoginButton onClick = {handleLoginClick} />
    }

    return (
        <div>
            <Greeting isLoggedIn={isLoggedIn} />
            {button}
        </div>
    )
}

 

 

위 코드를 보면 isLoggedIn의 값에 따라 button이라는 변수에 컴포넌트를 다르게 대입하는 것을 볼 수 있다

 

이렇게 컴포넌트가 대입된 변수를 return에 넣어 실제로 컴포넌트가 렌더링이 되도록 만들고 있다.

 

button이 설명은 컴포넌트라고 했지만, 실제로는 컴포넌트로부터 생성된 리액트 엘리먼트이다.

 

이렇게 별도로 변수를 선언해서 조건부 렌더링을 할 수도 있지만 Inline 조건문으로 코드를 더 간결하게 할 수 있다

 

 

4. inline 조건문

 

말 그대로 코드를 별도로 분리된 곳에 작성하지 않고, 해당 코드가 필요한 곳 안에 직접 집어 넣는다는 뜻

 

인라인 조건이라하면, 조건문을 코드 안에 집어넣는 것

 

4-1) inline if

 

inline if는 if문을 필요한 곳에 직접 집어 넣어서 사용하는 방법

 

실제로 if문을 넣는 것은 아니고, if문과 동일한 효과를 내기 위해 &&라는 논리 연산자를 사용

 

&& 연산자는 흔히 and 연산으로, 양쪽 조건문이 모두 true이면 전체 결과가 true

 

단축평가(short circuit evaluation)를 수행

 

첫번째 조건문이 false이면 어차피 전체 조건문은 false이므로 두번째 조건문은 평가하지 않는다

 

아래 그림은 첫번째 조건문의 값에 따른 && 연산자의 결과

 

 

 

조건문이 true이면 뒤에 나오는 expression이 평가되고,

 

조건문이 false이면 단축 평가에 의해 뒤에 나오는 expression을 평가하지 않는다.

 

리액트에서는 조건문이 true이면 오른쪽에 나오는 엘리먼트가 결과값이 되고, false이면 false가 결과가 된다.

 

inline if는 이 &&연산자를 JSX 코드에서 중괄호를 사용해 직접 넣어 사용하는 방법

 

function Mailbox(props) {
    const unreadMessages = props.unreadMessages;

    return (
        <div>
            <h1>안녕하세요!</h1>
            {unreadMessages.length > 0 &&
            <h2>
                현재 {unreadMessages.length}개의 읽지 않은 메시지가 있습니다.</h2>
                }
        </div>
    )
}

 

위 코드는 조건문 unreadMessages.length > 0의 값에 따라 뒤에 나오는

 

<h2> 태그로 둘러싸인 부분이 렌더링 되거나 안되거나 하게 된다.

 

&&연산자를 사용하는 이런 패턴은 단순하나, 리액트에서 굉장히 많이 사용하므로 기억해야한다.

 

한가지 주의할 점은 &&연산자를 사용시, 조건문에 Falsy를 사용하면 뒤에 나오는 expression은 평가되지 않지만,

 

Falsy expression의 결과가 그대로 나온다는 점이다.

 

예를 들어 아래 코드의 결과는 아무것도 안나오는 것이 아니라 count의 값인 0이 들어가서 <div>0</div>가 된다.

 

function Counter(props) {
    const count = 0

    return (
        <div>
            {count && <h1>현재 카운트: {count}</h1>}
        </div>
    )
}

 

 

4-2) inline if-else

 

inline if-else는 if-else문을 필요한 곳에 직접 넣어 사용하는 방법

 

inline if-else는 조건문의 값에 따라 다른 엘리먼트를 보여줄 때 사용

 

이를 위해 흔히 삼항 연산자로 부르는 ? 연산을 사용

 

? 연산자는 앞에 나오는 조건문이 true이면 첫번째 항목을 리턴, false이면 두번째 항목을 리턴한다.

 

조건문 ? (참일 경우) : (거짓일 경우)

 

실제로 사용해보면 다음과 같다

 

function UserStatus(props) {
    return (
        <div>
            이 사용자는 현재 <b>{props.isLoggedIn ? '로그인' : '로그인 하지 않은'}</b> 상태입니다.
        </div>
    )
}

 

위 코드에서 isLoggedIn이 true이면 '로그인', false이면 '로그인 하지 않은'이라는 문자열을 출력한다

 

아래처럼 문자열이 아닌 엘리먼트를 넣어 사용할 수도 있음

 

function LoginControl(props) {
    const [isLoggedIn, setIsLoggedIn] = useState(false)

    const handleLoginClick = () => {
        setIsLoggedIn(true)
    }

    const handleLogoutClick = () => {
        setIsLoggedIn(false)
    }

    return (
        <div>
            <Greeting isLoggedIn =  {isLoggedIn} />
            {isLoggedIn
            ? <LogoutButton onClick={handleLogoutClick} />
            : <LoginButton onClick={handleLoginClick} />
            }
        </div>
    )
}

 

isLoggedIn이 true인 경우 LogoutButton을 출력

 

false인 경우 LoginButton을 출력

 

인라인 조건문은 조건에 따라 각기 다른 엘리먼트를 렌더링

 

 

5. 컴포넌트 렌더링 막기

 

컴포넌트를 렌더링하고 싶지 않을때 어떻게 해야할까

 

null을 return하면 된다.

 

null을 return하면 리액트에서는 렌더링 되지 않는다.

 

function WarningBanner(props) {
    if (!props.warning) {
        return null
    }

    return (
        <div>경고!</div>
    )
}

 

WarningBanner라는 컴포넌트는 props.warning의 값이 false인 경우 null을 return

 

다시 말해 props.warning의 값이 true인 경우 경고 메시지를 출력

 

false인 경우 아무것도 출력하지 않는다.

 

function MainPage(props) {
    const [showWarning, setShowWarning] = useState(false)

    const handleToggleClick = () => {
        setShowWarning(prevShowWarning => !prevShowWarning)

    }

    return (
        <div>
            <WarningBanner warning={showWarning} />
            <button onClick={handleToggleClick}>
                {showWarning ? '감추기' : '보이기'}
            </button>
        </div>
    )
}

 

위 코드에 나오는 page 컴포넌트는 showwarning이라는 state의 값을 warningbanner 컴포넌트의 props로 전달해서,

 

showwarning의 값에 따라 경고문을 표시하거나 표시하지 않게 된다.

 

리액트는 특정 컴포넌트를 렌더링하고 싶지 않을 경우 null을 return하면 된다.

 

클래스 컴포넌트의 render() 함수에서 null을 return하는 것은 컴포넌트 생명주기에 전혀 영향을 미치지 않는다.

 

예를 들어 componentDidUpdate() 함수는 여전히 호출된다.

 

 

6. 실습으로 따라하기

 

$npx create-react-app my-app으로 react app 생성

 

src 폴더 내에 chapter_09라는 폴더 생성

 

Toolbar.jsx 파일 생성하고 함수 컴포넌트 작성

 

import React from 'react';

const styles = {
    wrapper: {
        padding: 16,
        display: 'flex',
        flexDirection: 'row',
        borderBottom: '1px solid grey'
    },
    greeting: {
        marginRight: 8,
    }
}

function Toolbar(props) {
    const {isLoggedIn, onClickLogin, onClickLogout} = props;

    return (
        <div style={styles.wrapper}>
            {isLoggedIn && <span style={styles.greeting}>환영합니다!</span>}

            {isLoggedIn ? (
                <button onClick={onClickLogout}>로그아웃</button>
            ): (
                <button onClick={onClickLogin}>로그인</button>
            )}
        </div>
    )
}

export default Toolbar;

 

사용자의 로그인 여부를 나타내는 isLoggedIn이라는 값을 props로 받아서 조건부 렌더링을 사용하여

 

환영 메시지를 표시하거나 감추고, 로그인/로그아웃 버튼을 보여주는 역할을 한다

 

&& 연산자와 ?연산자 등 2가지 종류의 연산자를 사용하여 조건부 렌더링을 구현

 

LandingPage.jsx라는 파일을 만들고 다음처럼 코드를 작성

 

import React,{useState} from 'react';
import Toolbar from './Toolbar';

function LandingPage(props) {
    const [isLoggedIn, setIsLoggedIn] = useState(false)

    const onClickLogin = () => {
        setIsLoggedIn(true)
    }
    const onClickLogout = () => {
        setIsLoggedIn(false)
    }

    return (
        <div>
            <Toolbar
            isLoggedIn={isLoggedIn}
            onClickLogin={onClickLogin}
            onClickLogout={onClickLogout}/>

            <div style={{ padding: 16 }}>리액트는 즐거워!</div>
        </div>
    )
}

export default LandingPage

 

 

useState() 훅을 사용해서 사용자의 로그인 여부를 자체적으로 관리

 

이 값을 Toolbar 컴포넌트에 전달해서 로그인 여부에 따라 툴바에 적절한 사용자 인터페이스가 표시되도록 한다.

 

index.js 파일을 다음과 같이 수정

 

import LandingPage from './chapter_09/LandingPage'

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <LandingPage />
  </React.StrictMode>
);

 

npm start로 실행하면 다음과 같다

 

로그인 버튼을 누르면.. 로그인 상태가 true로 바뀌면서 로그아웃 버튼이 나온다

 

TAGS.

Comments