클릭하면 1씩 증가하고 우클릭하면 1씩 감소하는, 그러면서 0 아래로는 떨어지지 않는 컴포넌트 3개를 만들어봤다.
들어가기 전에 리액트 컴포넌트 간단 정리
- 리액트가 변경된 속성값, 상태값을 기반으로 UI 자동 갱신
-
가상 돔(Virtual DOM)을 통한 빠른 UI 갱신
- DOM 계산 비용이 비싸기 때문
- 가상 돔을 이용해 이전 UI 상태를 메모리에 유지하고 변경될 UI 최소 집합을 계산
-
props: 속성값. 읽기 전용이며 불변 객체
- 컴포넌트 생성 시, 부모 컴포넌트로부터 전달받음
- 부모가 props를 변경하면(부모 입장에서는 state), 변경된 props 값을 참조하는 UI가 자동으로 업데이트
-
state: 상태값
- 각 컴포넌트가 개별로 생성/유지하는 상태값들
- 주로 컴포넌트 단위로 UI에 반영할 값을 저장할 목적
- 불변 객체로 처리해야 함. immer 패키지를 이용해 보다 편리하게 처리할 수 있음
- 상태값은 setter 함수로 변경해야 함. 직접 변경은 가급적 지양
- Element Tree
- Component Tree
만든 컴포넌트 소스코드
// App.js
import React from 'react';
import 'App.css';
import PropTypes from "prop-types";
import Counter from "Counter";
class App extends React.Component {
render() {
// defaultProps로 color: 'red'를 지정했기 때문에
// 컬러를 지정하지 않은 컴포넌트는 자동으로 빨간색이 된다.
return (
<div>
<Counter />
<Counter color="green" />
<Counter color="blue" />
</div>
)
}
}
export default App;
// Counter.js
import React from "react";
import PropTypes from "prop-types";
class Counter extends React.Component {
static defaultProps = {
color: 'red',
}
// defaultProps를 지정해주었으므로
// (= props가 있다면) type을 지정해주는 편이 좋음
static propTypes = {
color: PropTypes.string,
}
state = {
color: this.props.color,
value: 0,
}
onClick = () => {
this.setState(prevState => ({
value: prevState.value + 1
}))
}
// 마우스 우클릭 시 이벤트 onContextMenu
onContextMenu = (e) => {
// 원래 이벤트 동작을 막겠다는 의미
// onContextMenu(마우스 우클릭)에서 원래 이벤트 동작은 context 메뉴가 뜨는 것
e.preventDefault();
this.setState(prevState => ({
// 3항 연산자를 사용해 숫자가 0 아래로는 떨어지지 않게 처리
value: (prevState.value >= 1 ? prevState.value - 1 : 0),
}))
}
// backgroundColor: color 속성 추가를 위해 style 객체를 펼쳐서(...) 사용
// 다른 속성을 추가해줄 게 아니라면 style = { style }로만 써도 됨
render() {
const { color, value } = this.state;
return (
<div onClick={this.onClick}
style={ {...style, backgroundColor: color} }
onContextMenu={this.onContextMenu}>
{ value }
</div>
)
}
}
const style = {
width: '100px',
height: '100px',
display: 'inline-block',
borderRadius: '50px', // 리액트에서는 언더스코어가 허용되지 않기 때문에 카멜케이스로 작성
textAlign: 'center',
lineHeight: '100px', // 원지름이 줄높이가 되도록
userSelect: 'none', // 클릭했을 때 음영으로 영역선택되는 것 막기
fontSize: '3rem',
color: 'white',
margin: '1rem',
};
export default Counter;
onContextMenu
에서 기본 동작을 막지 않으면(e.preventDefault();
) 우클릭할 때마다 익히 아는 이 메뉴가 뜬다.
참고) 이벤트 핸들러 여러 방식으로 표현하기
전부 같은 동작을 한다.
onClick = () => {
this.setState({ value: value + 1 });
}
onClick = () => {
this.setState(({ value }) => ({
value: value + 1
}))
}
onClick = () => {
this.setState(({ value: prevState }) => ({
value: prevState + 1
}))
}
onClick = () => {
this.setState(prevState => ({
value: prevState.value + 1
}))
}