React Class Compnent & This Binding

류지승·2024년 10월 16일
0

React

목록 보기
11/19
post-thumbnail

Class Component & this

Class?

class란 반복되는 함수 또는 변수 모음집이라고 생각하면 된다. class는 new 연산자를 통해 새로운 instance들을 생성한다. class에서 함수와 변수를 사용할 때는 function, let, const 키워드를 사용하지 않는다. class 내에서의 function을 메소드라고 한다. 클래스가 사용하는 이유는 코드 재사용성 / 캡슐화 / 상속 / 유지보수 용이성이 있다.

Javascript는 객체 지향 프로그래밍(OOP - Object oriented programming)

class Monster {
  // class의 변수 작성
  power = 50;
  
  // class의 함수(메소드) 작성
  attack() {
    console.log("공격합니다!!");
  }
}
// instance monster1 생성
const monster1 = new Monster();
console.log(monster1.power); // 50

클래스에서의 상속

class에서의 공통 기능은 extends를 이용하여 상속한다.

class Monster {
  power = 50;
  attack() {
    console.log("공격합니다!!");
  }
}

// 클래스 상속 : 기존 클래스에 기능을 추가해서 만든 클래스
class SuperMonster extends Monster {
  run() {
    console.log("도망가자!!");
  }

  // 오버라이딩: Monster의 메소드를 다시 정의해서 덮어씌운다.
  attack() {
    conole.log("슈퍼 몬스터 필살기!!");
  }
}

// 인스턴스
const monster = new Monster();
console.log(monster.power); // 50
monster.attack(); // 공격합니다!!

// 인스턴스
const supermonster = new SuperMonster();
console.log(supermonster.power); // 50
supermonster.attack(); // 슈퍼 몬스터 필살기!!

this binding

javascript에서의 this binding은,, 실행 컨텍스트에서 코드 평가 과정에서 함수 호출 방식에 의해 동적으로 할당된다.

1. 전역 컨텍스트(Global Context)

전역 컨텍스트에서의 this전역 객체를 가리킨다.

// 전역 컨텍스트
console.log(this); // 브라우저에서는 window, Node.js에서는 global

2. 함수 호출 방식(Function Context)

일반 함수 호출에서의 this전역 객체를 가리킨다.

// 일반 함수
function showThis() {
    console.log(this);
}
showThis(); // window (브라우저), global (Node.js), strict mode에서는 undefined

3. 메서드 호출 방식 (Method Context)

객체의 메서드로 호출할 때, this는 해당 메서드를 호출한 객체를 가리킨다.

const obj = {
    value: 42,
  // 객체의 메서드
    getValue: function() {
        console.log(this.value);
    }
};
obj.getValue(); // 42, this는 obj를 가리킴

4. 생성자 호출 방식 (Constructor Context)

생성자 함수에서 this새로 생성된 인스턴스를 가리킨다.

// 생성자 함수
function Person(name) {
    this.name = name;
}
const person = new Person("John");
console.log(person.name); // John

5. call, apply, bind를 이용한 명시적 바인딩

call과 apply: 함수 호출 시 this를 명시적으로 지정할 수 있다.
bind: 함수의 this를 영구적으로 고정할 수 있다.

function showThis() {
    console.log(this.name);
}
const obj = { name: "Alice" };

showThis.call(obj); // Alice
showThis.apply(obj); // Alice

const boundFunc = showThis.bind(obj);
boundFunc(); // Alice

6. 화살표 함수(Arrow Function)

화살표 함수는 상위 스코프의 this를 상속한다.
즉, 동적으로 바인딩되지 않고 정적으로 결정된다.

const obj = {
    name: "Bob",
    getName: () => {
        console.log(this.name);
    }
};
obj.getName(); // undefined global window, 화살표 함수는 상위 스코프의 this를 사용 (전역 객체)

7. 이벤트 핸들러에서의 this (DOM Context)

DOM 이벤트 핸들러에서 this이벤트가 발생한 요소를 가리킨다.

document.querySelector("button").addEventListener("click", function() {
    console.log(this); // 클릭된 버튼 요소
});

8. class 문법에서의 this

클래스 내부의 메서드에서 this해당 클래스의 인스턴스를 가리킨다.

class Person {
    constructor(name) {
        this.name = name;
    }
    sayHello() {
      // 여기서의 this는 class Person의 instance인 person을 가리킨다. 즉 this.name = person.name
        console.log(`Hello, ${this.name}`);
    }
}
const person = new Person("Charlie");
person.sayHello(); // Hello, Charlie

9. setTimeout과 setInterval에서의 this

setTimeout과 setInterval의 콜백 함수에서 this전역 객체를 가리킨다.
화살표 함수를 사용하거나, bind를 통해 this를 고정할 수 있다.

const obj = {
    value: "Delayed Hello",
    greet: function() {
        setTimeout(function() {
            console.log(this.value); // undefined global window -> 전역 객체를 가리킴
        }, 1000);
    }
};
obj.greet();

// 화살표 함수를 사용하여 this를 바인딩
setTimeout(() => {
    console.log(this.value); // Delayed Hello
}, 1000);
/** @format */

'use client'

import { Component, ReactNode } from 'react'

export default class ClassCouterPage extends Component {
	state = {
		count: 1,
	}
	// onClickCount() {
	// 	  this.setState(this.state.count + 1)
	// }
// 이게 this binding 문제가 있는 이유
// this binding은 일반적으로 동적으로 바인딩 된다.
// 위 onClickCount는 객체의 메서드를 호출했기 때문에 해당 메서드는 호출한 객체를 가리킨다.
// 우리가 원하는 건 class를 가리키는 것 즉, 상위 스코프를 가리키게 해야하기 때문에
// 화살표 함수나 bind를 이용하여 this를 정적으로 지정한다.
	onClickCount = () => {
		this.setState(this.state.count + 1)
	}
	render(): ReactNode {
		return (
			<>
				<div>{this.state.count}</div>
				<button onClick={this.onClickCount}>카운트</button>
			</>
		)
	}
}

클래스형 컴포넌트 생명주기

  1. 그리기(render) -> render
  2. 그리고 난 뒤(render 직후 - useEffect []) -> componentDidMount
  3. 그리고 난 뒤 변경됐을 때(re-render) -> componentDidUpdate
  4. 그리고 난 뒤 사라질 때(clean up) -> componentWillUnMount
/** @format */

'use client'

import { Component, ReactNode } from 'react'

export default class ClassCouterPage extends Component {
	state = {
		count: 1,
	}
	// 컴포넌트 최초 렌더링 이후 실행
	// function Component 예시
	// useEffect(() => {}, []);
	componentDidMount(): void {}

	// 컴포넌트 리렌더링 후 호출
	// function Component 예시
	// useEffect(() => {}, [dependency]);
	componentDidUpdate(): void {}

	// 컴포넌트 삭제될 때 호출
	// function Component 예시
	// cleanUp function일 때 (타이머 함수 끝낼 때)
	// useEffect(() => {
	// return () => {};
	//}, [dependency]);
	componentWillUnmount(): void {}

	onClickCount = () => {
		this.setState(this.state.count + 1)
	}
	render(): ReactNode {
		return (
			<>
				<div>{this.state.count}</div>
				<button onClick={this.onClickCount}>카운트</button>
			</>
		)
	}
}

함수형 컴포넌트 생명주기

class형 컴포넌트같은 경우 생명주기를 관리하는 게 3가지가 있다. componentDidMount / componentDidUpdate / componentWillUnMount function형 컴포넌트 같은 경우 useEffect를 이용하여 생명주기 전체를 관리할 수 있다.
다음은 위에 class component를 function component로 변경한 예시이다.

/** @format */
'use client'

import { useEffect, useState } from 'react'

export default function Page() {
	const [count, setCount] = useState(1)

	useEffect(() => {
		console.log('그려지고 나서 실행')
		return () => {
			console.log('사라지기 전에 실행')
		}
	}, [count])

	const onClickCount = () => {
		setCount((prev) => prev + 1)
	}

	return (
		<>
			<div>{count}</div>
			<button onClick={onClickCount}>카운트</button>
		</>
	)
}

잘못된 useEffect 사용

  1. useEffect 내에는 setState를 되도록이면 사용하면 안된다.
useEffect(() => {
	setState(1);
}, [])

위 같은 경우 useEffect 같은 경우 렌더링이 된 직후에 실행되는데 state값을 변동하게 되면 리렌더가 발생한다. 즉 렌더링이 두번되는 것이다.

  1. useEffect dependency array에 state와 useEffect 코드 블록 내에 setState를 동시에 넣으면 안된다.
useEffect(() => {
	setCount((prev) => prev + 1);
}, [count])

위 같은 경우 count가 변경되면 -> useEffect 실행 -> setCount 실행 -> count 변경 이렇게 무한루프가 발생할 것이다.

profile
성실(誠實)한 사람만이 목표를 성실(成實)한다

0개의 댓글

관련 채용 정보