이벤트 처리와 조건부 렌더링

HyunHo Lee·2021년 12월 13일
0

프론트

목록 보기
40/56
post-thumbnail

이벤트 처리

React Element에서 이벤트를 처리하는 방식은 DOM Element에서 이벤트를 처리하는 방식과 매우 유사하다.

React의 이벤트는 소문자 대신 CamelCase를 사용하고, JSX를 사용하여 문자열이 아닌 함수로 이벤트 핸들러를 전달한다는 차이점이 있다.


//HTML
<button onclick="activateLasers()">
  Activate Lasers
</button>

// React
<button onClick={activateLasers}>
  Activate Lasers
</button>

React에서는 onclickonClick으로 사용하고 있다.


//HTML
<form onsubmit="console.log('You clicked submit.'); return false">
  <button type="submit">Submit</button>
</form>

HTML에서 폼을 제출할 때 가지고 있는 기본 동작을 방지하기 위해 return false를 해주었다. React에서는 false를 반환해도 기본 동작을 방지할 수 없다.

// React
function Form() {
  function handleSubmit(e) {
    e.preventDefault();
    console.log('You clicked submit.');
  }

  return (
    <form onSubmit={handleSubmit}>
      <button type="submit">Submit</button>
    </form>
  );
}

React에서는 e.preventDefault을 반드시 명시적으로 호출해야 한다.

( e는 합성 이벤트이다. )

토글

class Toggle extends React.Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};

    // This binding is necessary to make `this` work in the callback
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState(prevState => ({
      isToggleOn: !prevState.isToggleOn
    }));
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}

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

Class Component를 정의할 때, 일반적인 패턴은 이벤트 핸들러를 클래스의 메서드로 만드는 것이다. 현재 Toggle Class Component 안에 handleClick() 함수가 존재한다.

  • 동작
  1. isToggleOn는 초기값이 true이다.

  2. 자바스크립트에서 클래스 메서드는 바인딩이 되어있지 않다. handleClick 함수를 사용하기 위해 바인딩 해주었다. ( 바인딩 하지 않으면 함수가 호출될 때 this는 undefined )
    onClick={this.handleClick}과 같이 뒤에 ()를 사용하지 않고 메서드를 참조할 경우 바인딩 하면 된다.

  3. handleClick 함수에서는 클릭하면 isToggleOn 값이 not으로 바뀌도록 하고있다.

  4. 마지막으로 버튼에 onClick 이벤트를 주고, 버튼 요소안의 값을 삼항 연산자로 설계했다.


class LoggingButton extends React.Component {
  // 이 문법은 `this`가 handleClick 내에서 바인딩되도록 합니다.
  // 주의: 이 문법은 *실험적인* 문법입니다.
  handleClick = () => {
    console.log('this is:', this);
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        Click me
      </button>
    );
  }
}

실험적인 퍼블릭 클래스 필드 문법을 사용하여 bind 호출을 해결할 수 있다.


class LoggingButton extends React.Component {
  handleClick() {
    console.log('this is:', this);
  }

  render() {
    // 이 문법은 `this`가 handleClick 내에서 바인딩되도록 합니다.
    return (
      <button onClick={() => this.handleClick()}>
        Click me
      </button>
    );
  }
}

bind 호출을 하지 않기 위한 다른 해결 방법으로는 화살표 함수를 사용하는 것이다. 하지만 LoggingButton이 렌더링될 때마다 다른 콜백이 생성되는 문제점이 있다.


  • 이벤트 핸들러에 인자 전달하기
//화살표 함수
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>

//Function.prototype.bind 사용
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>

React 이벤트를 나타내는 e 인자가 ID 뒤에 두 번째 인자로 전달된다. 화살표 함수를 사용하면 명시적으로 인자를 전달해야 하지만 bind를 사용할 경우 추가 인자가 자동으로 전달된다.


조건부 렌더링

React에서는 원하는 동작을 캡슐화하는 Component를 만들 수 있다. 이렇게 해서 애플리케이션의 상태에 따라 Component 중 몇 개만 렌더링할 수 있다.


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

ReactDOM.render(
  // Try changing to isLoggedIn={true}:
  <Greeting isLoggedIn={false} />,
  document.getElementById('root')
);

isLoggedIn의 불린값에 따라 UserGreeting 또는 GuestGreeting 가 렌더링된다.


  • Element 변수
  render() {
    const isLoggedIn = this.state.isLoggedIn;
    let button;
    if (isLoggedIn) {
      button = <LogoutButton onClick={this.handleLogoutClick} />;
    } else {
      button = <LoginButton onClick={this.handleLoginClick} />;
    }

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

변수에 Element를 저장할 수 있다. isLoggedIn 불린값에 따라 button에 다른 값이 저장되고, return할때 사용된다.


  • 논리 && 연산자로 If를 인라인으로 표현하기
function Mailbox(props) {
  const unreadMessages = props.unreadMessages;
  return (
    <div>
      <h1>Hello!</h1>
      {unreadMessages.length > 0 &&
        <h2>
          You have {unreadMessages.length} unread messages.
        </h2>
      }
    </div>
  );
}

const messages = ['React', 'Re: React', 'Re:Re: React'];
ReactDOM.render(
  <Mailbox unreadMessages={messages} />,
  document.getElementById('root')
);

자바스크립트에서 true && expression은 항상 expression으로 평가되고 false && expression은 항상 false이다.

이 특징을 사용하여 길이가 0일때는 스크린에 나타나지 않고 0보다 클 때(값이 있을때만) 스크린에 h2태그를 나타내는 것이다.

render() {
  const count = 0;
  return (
    <div>
      { count && <h1>Messages: {count}</h1>}
    </div>
  );
}

하지만 조건이 false일 경우 falsy표현식을 반환한다. 이 예제어서는 <div>0</div>render 메서드에서 반환된다.


  • 조건부 연산자로 if-else구문 인라인으로 표현하기
render() {
  const isLoggedIn = this.state.isLoggedIn;
  return (
    <div>
      {isLoggedIn
        ? <LogoutButton onClick={this.handleLogoutClick} />
        : <LoginButton onClick={this.handleLoginClick} />
      }
    </div>
  );
}

조건부 연산자로 if-else처럼 사용하는 것이다. 조건이 너무 복잡한 것은 Component를 분리하는것도 고려하자.


  • Component가 렌더링하는 것을 막기

다른 Component에 의해 렌더링될 때 Component 자체를 숨기고 싶은 경우가 있다. 렌더링 결과를 출력하는 대신 null을 반환하여 해결하자.

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(state => ({
      showWarning: !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')
);

WarningBanner에서 props.warn가 false면 null을 리턴해주고있다. 렌더링시키고 싶지 않은 부분에서는 이와 같이 처리해줄 수 있다. 생명주기 메서드 호출에도 영향을 주지 않는다. 그 예로 componentDidUpdate는 계속해서 호출되게 된다.

profile
함께 일하고 싶은 개발자가 되기 위해 달려나가고 있습니다.

0개의 댓글