1. 컴포넌트 종류
컴포넌트를 선언하는 방식은 두가지이다.
1. 함수 컴포넌트
2. 클래스형 컴포넌트
이렇게 두가지가 있다. 하지만 최근 클래스형 컴포넌트보단 함수 컴포넌트를 주로 사용한다.
다양한 이유가 있겠지만, 주요 이유는
1. 간단한 작성으로 가독성이 좋다.
2. 성능적인 부분에서 함수가 좀 더 효율적으로 사용할 수 있다.
3. 테스트 용이성
2. 컴포넌트 생성
생성 순서는 1. 파일 만들기 -> 2. 코드 작성하기 -> 3. 모듈 내보내기 및 불러오기
컴포넌트를 만들고자 할 때는 컴포넌트 코드를 선언해야 한다.
const My Component = () => {
return <div> 함수형 컴포넌트 </div>;
};
export default MyComponent;
3. 화살표 함수
function BlackDog() {
this.name = '흰둥이';
return {
name : '검둥이',
bark: function() {
console.log(this.name + ': 멍멍!')
}
}
}
const blackDog = new BlackDog();
blackDog.bark(); // 검둥이: 멍멍!
function WhiteDog(){
this.name = '흰둥이';
return {
name: '검둥이',
bark: () => {
console.log(this.name + ': 멍멍');
}
}
}
const whiteDog = new WhiteDog();
whiteDog.bark(); // 흰둥이: 멍멍!;
function()을 사용했을 때는 검둥이가 나타나고, () => 를 사용했을 때는 흰둥이가 나타난다.
일반 함수는 자신이 종속된 객체를 this로 가리키고, 화살표 함수는 자신이 종속된 인스턴스를 가리킨다.
인스턴스란
"인스턴스"라는 용어는 주로 클래스 컴포넌트에서 사용된다. 인스턴스는 클래스의 실제 객체를 의미하며, 컴포넌트 클래스를 인스턴스화하여 사용하는 것을 말한다.
하지만 최근 함수 컴포넌트를 자주사용하여, 함수 컴포넌트에는 적용이 되지 않는다.
4. props
App.js
import './App.css';
import MyComponent from './MyComponent';
function App() {
return <MyComponent name={'react'} />
};
export default App;
MyComponent.js
const MyComponent = props => {
return <div> 안녕하세요, 제 이름은 {props.name} 입니다. </div>
};
export default MyComponent;
5. props 기본값 설정:defaultProps
props 값을 따로 지정하지 않았을 때 보여줄 기본 값을 설정하는 defaultProps다.
App.js
import './App.css';
import MyComponent from './MyComponent';
function App() {
return <MyComponent/>
};
export default App;
MyComponent.js
const MyComponent = props => {
return <div> 안녕하세요, 제 이름은 {props.name} 입니다. </div>
};
MyComponet.defaultProps = {
name: '기본 이름'
};
export default MyComponent;
위와 같이 설정을 하면 name에 기본이름이 출력 된다.
6. 태그 사이의 내용을 보여 주는 children
App.js
import './App.css';
import MyComponent from './MyComponent';
function App() {
return <MyComponent>리액트</MyComponent>
};
export default App;
MyComponent.js
const MyComponent = props => {
return (
<div>
안녕하세요, 제 이름은 {props.name} 입니다.
children 값은 {props.children} 입니다.
</div>
};
);
MyComponet.defaultProps = {
name: '기본 이름'
};
export default MyComponent;
위와 같이 children은 저렇게 태그 사이의 값을 보여준다.
props.children자리에 리액트가 들어간다.
7. 비구조화 할당 문법을 통해 props 내부 값을 추출
MyComponent.js
const MyComponent = ({ name, children }) => {;
return (
<div>
안녕하세요, 제 이름은 {name} 입니다.
children 값은 {children} 입니다.
</div>
};
);
MyComponet.defaultProps = {
name: '기본 이름'
};
export default MyComponent;
위와 같이 코드를 작성하게 된다면 name, children 값을 더 짧은 코드로 사용할 수 있다.
객체에서 값을 추출하는 문법을 비구조화 할당이라고 한다.
이 문법은 구조 분해 문법이라고 불리며, 함수의 파라미터 부분에서도 사용할 수 있다.
만약 함수의 파라미터가 객체라면 그 값을 바로 비구조화해서 사용할 수 있다.
8. propTypes를 통한 props 검증
MyComponent.js
import PropTypes from 'prop-types';
const MyComponent = ({ name, children }) => {
return (
<div>
안녕하세요, 제 이름은{name}입니다. <br />
children 값은 {children}입니다.
</div>
);
};
MyComponent.defaultProps = {
name: '기본이름'
};
MyComponent.propTypes = {
name: PropTypes.string
}
export default MyComponent;
App.js
import './App.css';
import MyComponent from './MyComponent';
function App() {
return <MyComponent name={3}>리액트</MyComponent>
};
export default App;
위 코드를 입력하게 되면 오류가 출력된다. 이유는 개발자에게 propTypes가 잘못되었다고 알려준다.
수정은 아래와 같이 하면 된다.
App.js
import './App.css';
import MyComponent from './MyComponent';
function App() {
return <MyComponent name="react"}>리액트</MyComponent>
};
export default App;
9. isReqired를 사용하여 필수 propTypes 설정
propTypes를 지정하지 않았을 때 경고 메세지를 띄어주는 작업을 할 것이다. propTypes를 지정할 때 뒤에 isRequired를 붙여 주면 된다.
MyComponent.js
import PropTypes from 'prop-types';
const MyComponent = ({ name, favoriteNumber, children }) => {
return (
<div>
안녕하세요, 제 이름은{name}입니다. <br />
children 값은 {children}입니다.<br />
제가 좋아하는 숫자는 {favoriteNumber} 입니다.
</div>
);
};
MyComponent.defaultProps = {
name: '기본이름'
};
MyComponent.propTypes = {
name: PropTypes.string,
favoriteNumber: PropTypes.number.isRequired
}
export default MyComponent;
위와 같이 작성을 했다면
이렇게 오류가 나온다. 이를 고치려면
App.js
import './App.css';
import MyComponent from './MyComponent';
function App() {
return <MyComponent name={'React'} favoriteNumber={1}>리액트</MyComponent>
};
export default App;
이렇게 고치면 된다.
10. 더 많은 PropTypes 종류
여러 가지 종류가 있다.
- array : 배열
- arrayOf(다른 PropType) : 특정 PropType으로 이루어진 배열을 의미한다. 예를 들면 arrayOf(PropTypes.number)는 숫자로 이루어진 배열이다.
- bool : true 혹은 false 값
- func : 함수
- number : 숫자
- object : 객체
- string : 문자열
- symbol : ES6의 Symbol
- node : 렌더링 할 수 있는 모든 것(숫자, 문자열, 혹은 jsx코드, children도 node PropType 이다.)
- instanceOf(클래스) : 특정 클래스의 인스턴스(예. instanceOf(MyClass))
- oneOf(['dog', 'cat']) : 주어진 배열 요소 중 값 하나
- oneOfType([React.PropTypes.string, PropTypes.number]) : 주어진 배열 안의 종류 중 하나
- objectOf(React.PropTypes.number) : 객체의 모든 키 값이 인자로 주어진 PropType인 객체
11. 클래스형 컴포넌트에서 props 사용하기
MyComponent.js
import { Component } from 'react';
import PropTypes from 'prop-types';
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;
클래스형 컴포너트에서 defaultProps와 propTypes를 설정할 때 class 내부에 저장할 수 있다.
MyComponent.js
import { Component } from 'react';
import PropTypes from 'prop-types';
class MyComponent extends Component {
static defaultPropTypes = {
name: '기본이름'
};
static propTypes = {
name: PropTypes.string,
favoriteNumber: PropTypes.number.isRequired
};
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;
12. state
리액트에서 state는 컴포넌트 내부에서 바뀔 수 있는 값을 의미한다. props는 컴포넌트가 사용되는 과정에서 부모 컴포넌트가 설정하는 값이며, 컴포넌트 자신은 해당 props를 읽기 전용으로만 사용 할 수 있다. prop를 바꾸려면 부모 컴포넌트에서 바꿔야 한다. 예를 들면 현재 상황에서는 App 컴포넌트에서 MyComponent를 사용할 때 Props를 바꿔 줘야 값이 변경 될 수 있다. 반면 MyComponent에서는 전달받은 name 값을 직접 바꿀 수 없다.
13. 클래스형 컴포넌트의 state
counter.js
import{ Component } from 'react'
class Counter extends Component {
constructor(props) {
super(props);
// state의 초깃값 설정하기
this.state = { number: 0 };
}
render() {
const { number } = this.state; //state조회할때는 this.state로 조회
return (
<div>
<h1>{number}</h1>
<button
// onClick을 통해 버튼이 클릭되었을 때 호출할 함수를 저장
onClick={() => {
this.setState({ number: number + 1 });
// this.setState를 사용하여 state에 새로운 값을 넣을 수 있음
}}
>
+1
</button>
</div>
);
}
}
컴포넌트에 state를 설정할 때는 constructor 메서드를 작성하여 설정함
constructor는 컴포넌트의 생성자 메서드임. constructor를 작성할때는 반드시 super(props)를 호출해 주어야 함
이 함수가 호출되면 현재 클래스형 컴포넌트가 상속받고 있는 리액트 Component 클래스가 지닌 생성자 함수를 호출해줌
컴포넌트의 state는 객체 형식이어야 함 render 함수에서 현재 state를 조회할때는 this.state를 조회하면 됨
이벤트로 설정할 함수를 넣어 줄때는 화살표 함수 문법을 사용하여 넣어 주어야 함
this.setState 함수가 state 값을 바꿀 수 있게 해줌
14. state 객체 안에 여러 값이 있을 때
counter.js
import{ Component } from 'react'
class Counter extends Component {
constructor(props) {
super(props);
// state의 초깃값 설정하기
this.state = {
number: 0 ,
fixedNumber: 0
};
}
render() {
const { number, fixedNumber } = this.state; //state조회할때는 this.state로 조회
return (
<div>
<h1>{number}</h1>
<h2>바뀌지 않는 값 : {fixedNumber}</h2>
<button
// onClick을 통해 버튼이 클릭되었을 때 호출할 함수를 저장
onClick={() => {
this.setState({ number: number + 1 });
// this.setState를 사용하여 state에 새로운 값을 넣을 수 있음
}}
>
+1
</button>
</div>
);
}
}
state 안에 또 다른 값을 추가해 주면 됨
또한 값을 변경할때는 this.setState 함수는 인자로 전달된 객체 안에 들어 있는 값만 바꾸기 때문에 원하는 값만 변경해줌
15. state를 constructor에서 꺼내기
class Counter extends Component {
constructor(props) {
super(props);
// state의 초깃값 설정하기
this.state = {
number: 0 ,
fixedNumber: 0
};
}
constructor 메서드 말구도 state의 초기값을 지정해 줄 수 있음
이렇게 하면 constructor 메서드를 선언하지 않고도 state 초기값을 설정할 수 있음
16. this.setState에 객체 대신 함수 인자 전달하기
onClick = {() => {
// this.setState를 사용하여 state에 새로운 값을 넣을 수 있다.
this.setState({ number: number + 1});
this.setState({ number: this.state.number + 1});
}}
this.setState를 사용하여 state 값을 업데이트할 때는 상태가 비동기적으로 업데이트 됨
this.setState를 사용한다고 해서 state 값이 바로 바뀌지 않음
이를 해결하기 위해 this.setState를 사용할 때는 객체 대신에 함수를 인자로 넣어줌
this.setState((prevState, props_ => {
return {
// 업데이트하고 싶은 내용
}
}
prevState는 기존 상태이고, props는 현재 지니고 있는 props를 가리킴
업데이트 과정에서 props가 필요하지 않다면 생략해도 됨
<button
onClick={() => {
// this.setState((prevState) => {
// return {
// number: prevState.number + 1,
// };
// });
this.setState((prevState) => ({
number: prevState.number + 1,
}));
// 위 코드와 아래 코드는 완전히 똑같은 기능을 함
// 이 코드는 함수에서 바로 객체를 반환한다는 의미
}}
>
17. this.setState가 끝난 후 특정 작업 실행하기
setState를 사용하여 값을 업데이트하고 난 다음에 특정 작업을 하고 싶을 때는
setState의 두번째 파라미터로 콜백 함수를 등록하여 작업을 처리할 수 있음
this.setState(
{
number: number + 1,
},
() => {
console.log("방금 setState가 호출되었습니다");
}
);
18. 함수형 컴포넌트에서 useState 사용하기
리액트 16.8 이전 버전에서는 함수 컴포넌트에서 state를 사용 할 수 없다. 하지만 16.8 이후부터는 useState라는 함수를 사용하여 함수 컴포넌트에서도 state를 사용할 수 있게 되었다.
이 과정에서 Hooks라는 걸 사용하게 된다.
19. 배열 비구조화 할당
객체 비구조화 할당과 비슷하다. 배열 안에 들어 있는 값을 쉽게 추출할 수 있게 해주는 문법이다.
const array = [1, 2];
const [one, two] = array;
20. useState 사용하기
const Say = () => {
const [message, setMessage] = useState("");
const onClickEnter = () => setMessage("안녕하세요");
const onClickLeave = () => setMessage("안녕히 가세요");
return (
<div>
<button onClick={onClickEnter}>입장</button>
<button onClick={onClickLeave}>퇴장</button>
<h1>{message}</h1>
</div>
);
};
useState 함수의 인자에는 상태의 초기값을 넣어줌
클래스형 컴포넌트에서의 state 초기값과는 다르게 useState에서는 반드시 객체가 아니어도 상관없음
함수를 호출하면 배열이 반환되는데 첫번째 원소는 현재 상태이고, 두번째 원소는 상태를 바꾸어 주는 함수
이 두번째 원소는 세터(Setter) 함수라고 부름. 배열 비구조화 할당을 통해 이름을 자유롭게 정해 줄수 있음
21. 한 컴포넌트에서 useState 여러 번 사용하기
const Say = () => {
const [message, setMessage] = useState("");
const [color, setColor] = useState("black");
const onClickEnter = () => setMessage("안녕하세요");
const onClickLeave = () => setMessage("안녕히 가세요");
return (
<div>
<button onClick={onClickEnter}>입장</button>
<button onClick={onClickLeave}>퇴장</button>
<h1 style={{ color }}>{message}</h1>
<button style={{ color: "red" }} onClick={() => setColor("red")}>
빨강색
</button>
<button style={{ color: "green" }} onClick={() => setColor("green")}>
초록색
</button>
<button style={{ color: "blue" }} onClick={() => setColor("blue")}>
파랑색
</button>
</div>
);
};
22. state를 사용할 때 주의 사항
state 값을 바꾸어야 할 때는 setState 혹은 useState를 통해 전달받은 세터 함수를 사용해야 함
그렇기 때문에 배열이나 객체를 업데이트 해야하는 경우 사본을 만들고 그 사본에 값을 업데이트 한 후
그 사본의 상태를 setState 혹은 세터함수를 통해 업데이트 해야 함
// 객체 다루기
const object = { a: 1, b: 2, c: 3};
const nextObject = { ...object, b: 2};
// 배열 다루기
const array = [
{id: 1, value: true},
{id: 1, value: true},
{id: 1, value: true},
];
let nextArray = array.concat({ id: 4}); //새 항목 추가
nextArray.filter(item => item.id !== 2); //id가 2인 항목 제거
nextArray.map(item => (item.id === 1 ? {...item, value: false}: item));
// id가 1인 항목의 value를 false로 설정
객체에 대한 사본을 만들 때는 spread 연산자 불리는 ..을 사용하여 처리하고, 배열에 대한 사본을 만들 때는 배열의 내장 함수를 활용한다.
23. 정리
props와 state는 둘 다 컴포넌트에서 사용하거나 렌더링할 데이터를 담고 있으므로 비슷해 보일 수 있지만 역활은 매우 다름.
props는 부모 컴포넌트가 설정하고 state는 자체적으로 지닌 값으로 컴포넌트 내부에서 값을 업데이트할 수 있음
props를 사용한다고 해서 값이 무조건 고정적이지 않음
부모 컴포넌트의 state를 자식 컴포넌트의 props로 전달하고 자식 컴포넌트에서 특정 이벤트가 발생할 때 부모 컴포넌트의 메서드를 호출하면 props도 유동적으로 사용할 수 있음
새로운 컴포넌트를 만들때는 useState를 사용할 것을 권장함
코드가 더 간결해질 뿐만 아니라 리액트 개발팀이 함수형 컴포넌트와 Hooks를 사용하는 것이 주요 컴포넌트 개발 방식이 될것이라고 공지했기 때문
'React > 다시 공부하는 리액트' 카테고리의 다른 글
다시 공부하는 리액트 5장 ref.DOM 이름 달기 (0) | 2023.07.04 |
---|---|
다시 공부하는 리액트 4장 이벤트 핸들링 (0) | 2023.07.03 |
7장. 컴포넌트의 라이프사이클 메서드 (0) | 2023.06.22 |
6장. 컴포넌트 반복 (0) | 2023.06.21 |
4장. 이벤트 핸들링 (0) | 2023.06.20 |