본문 바로가기

React

리액트 this키워드 문제

testdome에서 재미난 리액트 문제를 발견했다. 지금은 비록 사용하지 않는 클래스 컴포넌트이지만, this키워드를 이해하기 좋은 문제라 생각하여 해설해보려고 한다.

 

 

 

 

 

문제는 일단 다음과 같다. ( 출처: testdome )

// React is loaded and is available as React and ReactDOM
// imports should NOT be used
const sampleOptions = [
  { id: "753", title: "This is the first option" },
  { id: "035", title: "This is the second option" }
];

class OptionsShower extends React.Component {
  constructor(props) {
    super(props);
    const { options } = props;
    this.state = { options, displayOptions: false };
  }

  displayOptions() {
    this.setState({
      options: this.state.options,
      displayOptions: !this.state.displayOptions
    });
  }

  render() {
    var options = null;
    if (this.state.displayOptions) {
      options = (
        <ul id="options">
          {this.state.options.map(option => (
            <li key={option.id}>{option.title}</li>
          ))}
        </ul>
      );
    }
    return (
      <div>
        <button onClick={this.displayOptions}>
          {this.state.displayOptions ? "Hide options" : "Show options"}
        </button>
        {options}
      </div>
    );
  }
}

document.body.innerHTML = "<div id='root'></div>";
const root = ReactDOM.createRoot(document.getElementById("root"));

root.render(<OptionsShower options={sampleOptions} />);

 

위와 같은 코드로 실행이 되면, type error가 발생한다.

왜 인지 살펴보고, 이 문제를 해결하기 위해서 어떻게 코드를 바꿀지 생각해보자.

 

 

 

 


 

 

class형 컴포넌트에서는 props, state, 생명주기 메소드를 가르키는 데 this키워드를 자주 사용한다.

 그렇기에, this가 무엇을 가르키는 지 계속해서 신경을 써야한다.

 

import React from "react";
class App extends React.Component {
  componentDidMount() {
    console.log("componentDidMount의 this는", this); //App
  }

  render() {
    console.log("render()의 this는", this); //App
    return (
      <div>
        <h1 >Class Component this</h1>
      </div>
    );
  }
}
export default App;

위와 같은 코드에서는 this가 메소드를 호출한 해당 컴포넌트를 가리키고 있기 때문에 App 컴포넌트가 콘솔창에 잘 찍히고 있다.

 

이렇듯 컴포넌트에서 선언한 메소드들은 기본적으로 해당 컴포넌트를 바인딩하고 있다.

 

 

 

 

하지만, 다음과 같이 this를 지정하면 어떻게 될까?

import React from "react";
class App extends React.Component {
  state = {
    num: 0
  };
  componentDidMount() {
    console.log("componentDidMount의 this는", this); //App
  }
  click = () => {
    console.log("click함수 this", this);
  };

  render() {
    console.log("render()의 this는", this); //App
    return (
      <div>
        <h1 onClick={this.click}>Class Component this</h1>
      </div>
    );
  }
}
export default App;

 

위와 같이 코드를 작성해, h1태그를 클릭하여 click함수를 작동시키면, 아래와 같은 TypeError가 발생한다.

 

TypeError

 

 

왜 일까? 

 

 

TypeError이므로, 우리는 click함수 내에서 this가 해당 컴포넌트를 가르키고 있지 않아 발생하는 오류임을 짐작할 수 있다. 

this키워드가 일반 함수에서는 함수가 호출되는 방식에 따라 달라지므로, 콘솔창에 찍어보면 undefined가 찍힘을 알 수 있다.

 

 

 

 

 

 


 

 

 

화살표함수는 this가 상위 스코프 (렉시컬 스코프)를 가리킨다.

 

 

 

 

 

우리는 일전에 배운 this키워드에서 화살표함수에서의 this가 상위 스코프의 객체를 가리키며, bind와 같은 메소드를 사용할 수 없음을 배웠습니다.

따라서, 기존의 코드에서 click함수를 일반 함수가 아닌, 화살표함수로 바꾼다면 this가 상위 스코프인 App컴포넌트를 가리키므로 정상적으로 작동함을 알 수 있습니다. 

 

 

아래 코드는 click함수를 일반 함수에서 화살표함수로만 바꾸어줬습니다.

import React from "react";
class App extends React.Component {
  state = {
    num: 0
  };
  componentDidMount() {
    console.log("componentDidMount의 this는", this); //App
  }
  click = () => {
    console.log("click함수 this", this);
  };

  render() {
    console.log("render()의 this는", this); //App
    return (
      <div>
        <h1 onClick={this.click}>Class Component this</h1>
      </div>
    );
  }
}
export default App;

 

 

 

아래와 같이 App컴포넌트가 콘솔창에 잘 찍히고 있는 것을 알 수 있다.

 

 

 

 

 

 

 

그러니까 처음에 말한 문제의 정답은 displayOptions의 함수를 화살표함수로만 바꿔주면 된다.

 

 

 

 

 

 

 

 


다시 한 번 복기해보는 화살표함수에서의 this키워드 특징 ( this가 없다고 말해야 하나? )

  • 화살표 함수는 익명 함수로만 만들 수 있습니다.
  • 화살표 함수는 생성자로 사용할 수 없습니다.
  • 화살표 함수는 스스로의 this, argument 를 가지지 않습니다.
  • 함수가 정의된 스코프에 존재하는 this 를 가리킵니다.
  • 화살표 함수는 생성될 때 this 가 결정됩니다.
  • 화살표 함수가 어떻게 사용되건, 호출되건, this 는 바뀌지 않습니다.

 

 

다시 한 번 복기해보는 일반함수에서의 this키워드 특징

  • global scope 에서 사용될 때 this 는 전역 객체를 가리킵니다.(window 객체)
  • 함수에서 사용될 때에도 this 는 전역 객체를 가리킵니다.
  • 객체에 속한 메소드에서 사용될 때 this 는 메소드가 속한 객체를 가리킵니다.
  • 객체에 속한 메소드의 내부함수에서 사용될 때 this 는 전역 객체를 가리킵니다.
  • 생성자에서 사용될 때 this 는 이 생성자로 인해 생성된 새로운 객체를 가리킵니다.