함수형 컴포넌트
비교적 선언하기 편함
메모리 자원을 더 적게 사용함
클래스형 컴포넌트
state
, 라이프사이클 API 사용 가능
임의 메서드 정의 가능
하지만 리액트 업데이트 이후 Hooks
도입으로 함수형 컴포넌트도 클래스형 컴포넌트처럼 사용할 수 있게 됐다...! (나중에 자세히 다룰 것)
// src/MyComponent.js
import React from 'react';
const MyComponent = () => {
return <div>나의 새롭고 멋진 컴포넌트</div>;
};
export default MyComponent;
위는 함수형 컴포넌트로, 화살표함수를 사용해 작성해주었다. 마지막 줄에서 export
로 모듈을 내보내 다른 파일에서 이 컴포넌트를 사용할 수 있도록 한다. 예를 들어 App.js
에서 쓴다면...
// App.js
import React, { Component } from 'react';
import MyComponent from './MyComponent';
class App extends Component {
render() {
return <MyComponent />;
}
};
export default App;
App.js
는 클래스형으로 작성했다. 클래스형은 컴포넌트를import { Component } from 'react';
혹은 class App extends React.Component
으로 상속받아 정의할 수 있다.
render()
메서드는 클래스 컴포넌트에서 반드시 구현돼야하는 유일한 메서드로, this.props
와 this.state
의 값을 활용하여 return
한다.
props
는 properties를 줄인 표현으로 컴포넌트 속성을 설정할 때 사용하는 요소이다. props
는 해당 컴포넌트를 불러와 사용하는 부모 컴포넌트(App.js
같은)에서 설정할 수 있다.
우선 MyComponent
를 수정하여 name
이라는 props
를 렌더링하도록 설정해 보자. props
값은 컴포넌트 함수의 파라미터로 받아 와서 사용할 수 있다.
// src/MyComponent.js
import React from ‘react‘;
const MyComponent = props => {
return <div>안녕하세요, 제 이름은 {props.name}입니다.</div>;
};
MyComponent.defaultProps = {
name: '기본 이름'
};
export default MyComponent;
이렇게 props
를 정의해두고 위 컴포넌트가 필요로 하는 props
값을 App.js
에서 지정해 주면 된다. 사용하는 곳에서 지정해 주지 않으면, defaultProps
에서 설정한 값이 들어갈 것이다.
// App.js
import React from 'react';
import MyComponent from './MyComponent';
const App = () => {
return <MyComponent name="React" />;
};
export default App;
children
은 컴포넌트 태그 사이의 내용을 보여 주는 props
다.
// App.js
import React from 'react';
import MyComponent from './MyComponent';
const App = () => {
return <MyComponent>리액트</MyComponent>;
};
export default App;
// src/MyComponent.js
import React from 'react';
const MyComponent = props => {
return (
<div>
안녕하세요, 제 이름은 {props.name}입니다. <br />
children 값은 {props.children}
입니다.
</div>
);
};
MyComponent.defaultProps = {
name: '기본 이름'
};
export default MyComponent;
<MyComponent>
태그 사이에 있던 '리액트'가 props.children
으로 넘어간다.
비구조화 할당 문법은 객체에서 값을 추출하는 것을 말한다. 다시 말해 props.name
, props.children
이렇게 호출해야 했던 것들을 props
객체에서 값을 추출(비구조화)함으로써 아래와 같이 코드를 간단하게 만들 수 있다.
import React from 'react';
const MyComponent = ({ name, children }) => {
return (
<div>
안녕하세요, 제 이름은 {name}입니다. <br />
children 값은 {children}
입니다.
</div>
);
};
MyComponent.defaultProps = {
name: '기본 이름'
};
export default MyComponent;
컴포넌트의 필수적인 props
를 지정하거나 props
의 타입을 지정할 때는 propTypes
를 사용한다. import
구문을 필요로 한다.
import React from ‘react‘;
import PropTypes from ‘prop-types‘;
const MyComponent = ({ name, children }) => {
return (
<div>
안녕하세요, 제 이름은 {name}입니다. <br />
children 값은 {children}
입니다.
</div>
);
};
MyComponent.propTypes = {
name: PropTypes.string.isRequired,
children: PropTypes.number
};
export default MyComponent;
이렇게 지정을 해 두면 props.name
이 문자열이 아니거나, 값이 지정되지 않거나(isRequired
일 때), props.children
이 숫자가 아니면 콘솔창에서 오류가 뜬다. 이런 식으로 검증 가능!
PropTypes 종류
array, bool, func, number, object, string
arrayOf(other)
other로 이루어진 배열symbol
ES6의 Symbolnode
렌더링할 수 있는 모든 것(숫자, 문자열, JSX코드, props.children)instanceOf(class)
특정 클래스의 인스턴스oneOf(['a','b'])
주어진 배열 요소 중 값 하나oneOfType([PropTypes.string, PropTypes.instanceOf(class)])
주어진 배열 안 종류 중 하나objectOf(PropTypes.number)
특정 타입의 프로퍼티 값들을 갖는 객체shape({ name: PropTypes.string, num: PropTypes.number })
특정 형태를 갖는 객체any
아무 종류class MyComponent extends Component {
render() {
const { name, favoriteNumber, children } = this.props; // 비구조화 할당
return (
<div>
안녕하세요, 제 이름은 {name}입니다. <br />
children 값은 {children}
입니다.
<br />
제가 좋아하는 숫자는 {favoriteNumber}입니다.
</div>
);
}
}
MyComponent.defaultProps = {
name: ‘기본 이름‘
};
MyComponent.propTypes = {
name: PropTypes.string,
favoriteNumber: PropTypes.number.isRequired
};
export default MyComponent;
render
함수에서 this.props
를 조회하면 된다. 아래처럼 클래스 내부에서 defaultProps
와 propTypes
를 설정할 수도 있다.
import React, { Component } from ‘react‘;
import PropTypes from ‘prop-types‘;
class MyComponent extends Component {
static defaultProps = {
name: ‘기본 이름‘
};
static propTypes = {
name: PropTypes.string,
favoriteNumber: PropTypes.number.isRequired
};
render() {
const { name, favoriteNumber, children } = this.props; // 비구조화 할당
return (…);
}
}
export default MyComponent;
리액트에서 state
란 컴포넌트 내부에서 바뀔 수 있는 값을 의미한다. props
는 컴포넌트가 사용되는 과정에서 부모 컴포넌트가 설정하는 값이므로, 이를 바꾸려면 꼭 부모 컴포넌트에서 바꾸어 주어야 한다.
클래스형 컴포넌트에서 state
를 설정할 때는 아래와 같이 constructor
생성자 메소드를 작성해서 설정해줄 수 있다.
constructor(props) {
super(props);
this.state = {
number: 0,
fixedNumber: 0
};
}
super(props);
를 호출해 현재 클래스형 컴포넌트가 상속받고 있는 리액트의 component
클래스가 지닌 생성자 함수를 호출해야 한다. 그 후 this.state
객체에 초기 state
값을 설정해 준다.
state = {
number: 0,
fixedNumber: 0
};
...아니면 그냥 constructor
없이 설정해도 된다.
render() {
const { number, fixedNumber } = this.state;
return (
<div>
<h1>{number}</h1>
<h2>바뀌지 않는 값: {fixedNumber}</h2>
<button
onClick={() => {
this.setState({ number: number + 1 });
}}
>
+1
</button>
</div>
);
}
render
함수를 확인해보면 button
태그에 onClick
을 props
로 전달해서 이벤트를 설정했다. 이벤트로 설정할 함수를 넣어 줄 때는 화살표 함수 문법을 사용하여 넣어 주어야 한다. 설정된 함수 내부에서는 this.setState
라는 함수를 사용해서 state
값을 변경한다. 전달된 객체 안에 들어 있는 값만 바꾸어 주기 때문에 fixedNumber
는 바뀌지 않는 것!
this.setState
는 비동기함수이므로 아래와 같이 코드를 작성하면 +1이 두 번 되지 않는다.
onClick={() => {
this.setState({ number: number + 1 });
this.setState({ number: this.state.number + 1 });
}}
이를 해결하려면 아래 코드와 같이 객체 대신 함수를 인자로 넣어 주면 된다.
onClick={() => {
this.setState(prevState => {
return {
number: prevState.number + 1
};
});
// 위 코드와 아래 코드는 완전히 똑같은 기능을 함
// 아래 코드는 함수에서 바로 객체를 반환한다는 의미
// (코드블럭{} 생략, return 구문 생략)
this.setState((prevState, props) => ({
number: prevState.number + props
}));
// 업데이트하는 과정에서 prop이 필요하다면 전달
}}
여기서 prevState
는 기존 상태이고, props
는 현재 지니고 있는 props
를 가리킨다. 필요 없으면 위 코드와 같이 prevState
만 전달해도 된다.