해당 시리즈는 제목 그대로 김민준님의 '리액트를 다루는 기술'을 요약한 글입니다.
목차
3장 컴포넌트
4장 이벤트 핸들링
3장 컴포넌트
이 장에서는 먼저 클래스형 컴포넌트에 대해 살펴본 뒤, 컴포넌트를 새로 만들고 사용하는 방법을 알아보겠습니다. 그리고 컴포넌트의 속성 값을 지닌 props와 상태 값을 지닌 state를 사용하는 방법도 알아보겠습니다. - 84p
컴포넌트를 선언하는 방식
- 함수형
- 클래스형
// 함수형 컴포넌트
import React from 'react';
const App = () => {
return (
<div></div>
);
}
export default App;
// 클래스형 컴포넌트
import React, { Component } from 'react'
class App extends Component {
render() {
return (
<div></div>
)
}
}
export default App
리액트 공식 매뉴얼에서는 컴포넌트를 새로 작성할 때 함수형 컴포넌트와 Hooks를 사용하도록 권장하고 있습니다. 하지만 클래스형 컴포넌트가 사라지는 것은 아니므로 클래스형 컴포넌트의 기능은 꼭 알아 두어야 합니다.
함수형 컴포넌트의 장점은 다음과 같습니다.
- 클래스형 컴포넌트보다 선언하기가 편하다.
- 클래스형 컴포넌트보다 메모리 자원을 덜 사용한다.
- 빌드한 후 배포할 때도 결과물의 파일 크기가 더 작다.
주요 단점은 state와 라이프사이클 API의 사용이 불가능하다는 점이었는데 리액트 v16.8 업데이트 이후 Hooks라는 기능이 도입되면서 해결되었습니다.
props
properties를 줄인 표현으로 컴포넌트 속성을 설정할 때 사용
props 값은 해당 컴포넌트를 불러와 사용하는 부모 컴포넌트에서 설정
// App.js
import React from 'react';
import MyComponent from './MyComponent';
const App = () => {
return <MyComponent name="react" />;
};
export default App;
// MyComponent.js
import React from 'react';
const MyComponent = (props) => {
return <div>제 이름은 {props.name}입니다.</div>;
};
export default MyComponent;
부모 컴포넌트에서 props를 설정하지 않았을 때 사용할 기본값은 defaultProps를 사용해 설정 가능
import React from 'react';
const MyComponent = (props) => {
return <div>제 이름은 {props.name}입니다.</div>;
};
MyComponent.defaultProps = {
name: '홍길동',
};
export default MyComponent;
컴포넌트의 필수 props를 지정하거나 props의 타입(type)을 지정할 때는 propTypes를 사용
import React from 'react';
import PropTypes from 'prop-types';
const MyComponent = (props) => {
return (
<div>
<p>제 이름은 {props.name}입니다.</p>
<p>제일 좋아하는 숫자는 {props.favoriteNumber}입니다.</p>
</div>
);
};
MyComponent.defaultProps = {
name: '홍길동',
};
MyComponent.propTypes = {
name: PropTypes.string,
favoriteNumber: PropTypes.number.isRequired,
};
export default MyComponent;
children을 사용하면 태그 사이의 내용을 보여줄 수 있다.
import React from 'react';
import MyComponent from './MyComponent';
const App = () => {
return <MyComponent>I Love React</MyComponent>;
};
export default App;
import React from 'react';
const MyComponent = (props) => {
return (
<div>
<p>children은 {props.children}입니다.</p>
</div>
);
};
export default MyComponent;
비구조화 할당(구조 분해)를 사용하면 props 내부 값을 추출하여 더 편하게 사용할 수 있다.
import React from 'react';
import PropTypes from 'prop-types';
const MyComponent = (props) => {
const { name, favoriteNumber, children } = props;
return (
<div>
<p>제 이름은 {name}입니다.</p>
<p>제일 좋아하는 숫자는 {favoriteNumber}입니다.</p>
<p>children은 {children}입니다.</p>
</div>
);
};
MyComponent.defaultProps = {
name: '홍길동',
};
MyComponent.propTypes = {
name: PropTypes.string,
favoriteNumber: PropTypes.number.isRequired,
};
export default MyComponent;
import React from 'react';
import PropTypes from 'prop-types';
const MyComponent = ({ name, favoriteNumber, children }) => {
return (
<div>
<p>제 이름은 {name}입니다.</p>
<p>제일 좋아하는 숫자는 {favoriteNumber}입니다.</p>
<p>children은 {children}입니다.</p>
</div>
);
};
MyComponent.defaultProps = {
name: '홍길동',
};
MyComponent.propTypes = {
name: PropTypes.string,
favoriteNumber: PropTypes.number.isRequired,
};
export default MyComponent;
클래스형 컴포넌트에서 props를 사용할 때는 render 함수에서 this.props를 조회
import React, { Component } from 'react';
import PropTypes from 'prop-types';
class MyComponent extends Component {
render() {
const { name, favoriteNumber, children } = this.props;
return (
<div>
<p>제 이름은 {name}입니다.</p>
<p>제일 좋아하는 숫자는 {favoriteNumber}입니다.</p>
<p>children은 {children}입니다.</p>
</div>
);
}
}
MyComponent.defaultProps = {
name: '홍길동',
};
MyComponent.propTypes = {
name: PropTypes.string,
favoriteNumber: PropTypes.number.isRequired,
};
export default MyComponent;
state
리액트에서 state는 컴포넌트 내부에서 바뀔 수 있는 값을 의미
클래스형 컴포넌트의 state
import React, { Component } from 'react';
class Counter extends Component {
constructor(props) {
super(props);
this.state = {
number: 0,
fixedNumber: 0,
};
}
render() {
const { number, fixedNumber } = this.state;
return (
<div>
<h1>{number}</h1>
<h2>바뀌지 않는 값: {fixedNumber}</h2>
<button
onClick={() => {
this.setState({ number: number + 1 });
}}
>
+1
</button>
</div>
);
}
}
export default Counter;
state를 constructor에서 꺼내기
import React, { Component } from 'react';
class Counter extends Component {
state = {
number: 0,
fixedNumber: 0,
};
render() {
const { number, fixedNumber } = this.state;
return (...);
}
}
export default Counter;
this.setState에 객체 대신 인자 전달하기
this.setState를 사용하여 state 값을 업데이트할 때는 비동기적으로 상대가 업데이트 됩니다. 따라서, 다음과 같이 onClick에 설정한 함수 내부에서 this.setState를 두 번 호출하면 +2가 되는 것이 아니라 +1만 됩니다.
onClick={() => {
this.setState({ number: number + 1 });
this.setState({ number: this.state.number + 1 });
}}
이에 대한 해결책은 다음과 같이 객체 대신에 함수를 인자로 넣어 주는 것입니다.
onClick={() => {
this.setState(prevState => {
return { number: prevState.number + 1 }
});
// { }를 생략하면 값을 바로 리턴해줍니다.
this.setState(prevState => ({ number: prevState.number + 1 }));
}}
함수형 컴포넌트에서 useState 사용하기
- useState 함수의 인자에는 상태의 초깃값을 넣는다.
- 현재 상태와 상태를 바꾸어주는 함수를 리턴한다.
import React, { useState } from 'react';
const Counter = () => {
const [number, setNumber] = useState(0);
return (
<div>
<h1>{number}</h1>
<button onClick={() => setNumber(number + 1)}>+1</button>
</div>
);
};
export default Counter;
4장 이벤트 핸들링
이벤트 사용시 주의사항
- 이벤트 이름은 카멜 표기법으로 작성
- 함수 형태의 값을 전달 (HTML에서 큰따옴표를 사용해 이벤트를 설정하는 것과 다름)
- DOM 요소에만 이벤트 설정 가능 (컴포넌트에는 이벤트 설정 불가능)
import React, { Component } from 'react';
class EventPractice extends Component {
state = {
username: '',
message: '',
};
onChange = (e) => {
this.setState({
[e.target.name]: e.target.value,
});
};
onSubmit = () => {
alert(`${this.state.name}: ${this.state.message}`);
this.setState({
username: '',
message: '',
});
};
onKeyPress = (e) => {
if (e.key === 'Enter') {
this.onSubmit();
}
};
render() {
return (
<div>
<input
type="text"
name="username"
placeholder="사용자명을 입력하세요."
value={this.state.username}
onChange={this.onChange}
/>
<input
type="text"
name="message"
placeholder="메시지를 입력하세요."
value={this.state.message}
onChange={this.onChange}
onKeyPress={this.onKeyPress}
/>
<button onClick={this.onSubmit}>확인</button>
</div>
);
}
}
export default EventPractice;
함수형 컴포넌트로 구현해보기
import React, { useState } from 'react';
const EventPractice = () => {
const [form, setForm] = useState({
username: '',
message: '',
});
const { username, message } = form;
onChange = (e) => {
const nextForm = {
...form,
[e.target.name]: e.target.value,
};
setForm(nextForm);
};
onSubmit = () => {
alert(`${username}: ${message}`);
setForm({
username: '',
message: '',
});
};
onKeyPress = (e) => {
if (e.key === 'Enter') {
onSubmit();
}
};
return (
<div>
<input
type="text"
name="username"
placeholder="사용자명을 입력하세요."
value={username}
onChange={onChange}
/>
<input
type="text"
name="message"
placeholder="메시지를 입력하세요."
value={message}
onChange={onChange}
onKeyPress={onKeyPress}
/>
<button onClick={onSubmit}>확인</button>
</div>
);
};
export default EventPractice;
우리가 8장에서 배울 useReducer와 커스텀 Hooks를 사용하면 이 작업을 훨씬 더 편하게 할 수도 있습니다.
'WEB🔨 > React' 카테고리의 다른 글
리액트를 다루는 기술 요약#5 (0) | 2021.06.24 |
---|---|
리액트를 다루는 기술 요약#4 (0) | 2021.06.23 |
리액트를 다루는 기술 요약#3 (0) | 2021.06.21 |
리액트를 다루는 기술 요약#1 (0) | 2021.06.14 |
There might be a problem with the project dependency tree. (0) | 2020.12.26 |