그렇다 또 다시 우리의 오래된 친구 this
가 등장하였다.
왜 handleClick에서 this가 undefined가 뜨는 것인가? handleClick은 적어도 어떤 문맥안에 속해있는 것이라서 뭐라도 나올줄 알았는데 undefined가 나온다니?...
Javascript는 도통 이해할 수 가 없다. 쨌든 왜그런가?
Our cute Colt가 이에 대해서 답변을 남겼다. 아래의 링크로 가서 자세한 설명을 보라는 것이었다.
결론부터 얘기하면 this는 누가 호출했냐에 따라서 문맥이 달라진다는 것. 아래 예시를 보자.
class Dog {
constructor() {
this.favoriteWord = 'Woof!';
}
bark() {
return this.favoriteWord;
}
}
let dog = new Dog();
dog.bark(); // => Woof!
let bark = dog.bark;
// bark();
// => Error // in this case , 'this' inside 'bark' refers to 'window'
// because 'this' belongs to caller which is bark and bark belongs to 'window'
dog.bark안에 this가 가리키는 원래 존재는 Dog이다 그러나 bark 라는 새로운 변수에 담았고 호출하였더니 this는 window를 가리킨다. 왜냐면 let bark는 window에 속해있기 때문.
다음 예문을 보자.
let cat = {
favoriteWord: 'Meow!',
//mew: function(){
// return this.favoriteWord;
// }
};
cat.meow = bark;
cat.meow(); // meow
// This works because the this variable in the bark function isn't tied to the original dog object.
// So we can freely assign bark to cat.meow. And when we call cat.meow, the caller is cat, so this.favoriteWord refers to "Meow!" instead of "Woof!".
그래서 bark라는 메소드는 어디에다 붙여도 되는 객체가 되었다. cat안에 코멘트 쳐진 코드를 봐라. 그럼 bark의 this가 dog를 가리키게 하려면 어떻게 해야할까? bind함수를 사용하면 된다.
(참고로 bind는 여기서 다룬적이 있다. => https://velog.io/@yhko1992/getter%EC%99%80-setter%EC%99%80-call%ED%95%A8%EC%88%98)
let alwaysWoof = bark.bind(dog);
alwaysWoof(); // woof
// Why does this work? It's because calling bind on a function returns a copy of that function in which this is always set to whatever arg you pass to bind.
// This applies even if we change the caller of the bound function:
cat.meow = alwaysWoof;
cat.meow(); // => "Woof!"
dog를 bark 함수에 bind
시켜주면 어떠한 문맥에서도 this는 dog를 가리킨다.
그럼 리액트 컴포넌트안에서는 어떻게 되는 것일까?
class Welcome extends React.Component {
render() {
return <button onClick={this.sayName}>Say My Name</button>;
}
sayName() {
alert(this.props.name); // undefined
}
}
// But what React is doing behind the scenes is assigning this.sayName to another variable.
// That is, it's just like this:
let onClick = this.sayName;
onClick();
/*
And just like our dog example, we get an error.
Because this is undefined.
This is extra confusing because in previous versions of React, React would "autobind" the event handler for you, so it would work.
But at some point, Facebook decided to stop doing that, so ... here we are.
*/
리액트를 쓰면 Component라는 커튼 뒤에서 sayName 메소드가 onClick이라는 글로벌 변수에 할당하는 일이 벌어진다. 그니깐, Welcome이라는 문맥 밖에서 sayName을 찾으려고 한다는 말이다.
그럼, this(여기선 window?)안에는 당연히 sayName이 없으므로 undefined가 뜰것이다. (sayName은 Welcome 객체 안에만 등록되어 있으므로)
그렇다면! this를 로그했을때 window 객체를 출력해야하는데 this마저도 undefined이다. 그럼 도대체 sayName은 어떠한 문맥안에 있는 것인가.... 이게 아직 풀리지 않는다.
여튼, 사실 이전 리액트에서는 this가 자동으로 바인딩 되게 했는데 현재는 그렇게 하고 있지 않아서 수동으로 바인딩 해주면 된다.
포인트는, this가 더이상 Welcome을 가리키지않으니 바인딩을 수동으로 해줘야한다는 것!
끝!
출처: https://gist.github.com/fongandrew/f28245920a41788e084d77877e65f22f