2.1 src 디렉터리에 MyComponent.js 파일 생성
3.3 props 기본값 설정: defaultProps
3.5 비구조화 할당 문법을 통해 props 내부 값 추출하기
* isRequired를 사용하여 필수 propTypes 설정
애플리케이션의 사용자 인터페이스를 구성하는 요소 단위. 단순한 템플릿 이상의 기능을 갖추고 있다.
* 라이프사이클: 컴포넌트의 수명 주기. 페이지에 렌더링되기 전 준비 과정부터 페이지에서 사라질 때까지를 이른다. (7장에서 다룸)
import '/App.css';
function App() {
const name = '리액트';
return <div className="react">{name}</div>;
}
export default App;
import { Component } from 'react';
class App extends Component {
render() {
const name='react';
return <div className="react">{name}</div>;
}
}
export default App;
파일 만들기 ▶ 코드 작성하기 ▶ 모듈 내보내기 및 불러오기
MyComponent.js 파일을 열고 아래 코드를 작성한다.
// MyComponent.js
const MyComponent = () => {
return <div>나의 새롭고 멋진 컴포넌트</div>;
};
export default MyComponent;
자바스크립트 ES6에서 도입된 화살표 함수 선언 방식을 이용했다.
이렇게 선언한 컴포넌트도 함수형 컴포넌트가 된다.
* 화살표 함수 문법 ▶ https://corner-ds.tistory.com/15#ArrowFunc
모듈 내보내기 (export)
방금 작성한 코드의 맨 아래 줄이 다른 파일에서 이 파일을 import할 때 불러올 수 있는 함수를 지정하는 코드이다.
export default func1;
모듈 불러오기 (import)
App.js에서 MyComponent를 불러와서 사용하기 위해 수정한 코드.
// App.js
const App = () => {
return <MyComponent />;
};
export default App;
props는 properties의 줄임말로, 컴포넌트 속성을 설정할 때 사용하는 요소
props 값은 해당 컴포넌트를 불러와 사용하는 부모 컴포넌트에서 설정할 수 있다.
MyComponent 컴포넌트를 수정하여 해당 컴포넌트에서 name이라는 props를 렌더링하도록 설정해보자.
props 값은 컴포넌트 함수의 매개변수로 받아 와서 사용할 수 있다.
props를 렌더링할 때 에는 2장에서 다뤘던 대로 JSX 내부에서 { } 기호로 감싸 주면 된다.
// MyComponent.js
const MyComponent = props => {
return <div>안녕하세요, 제 이름은 {props.name}입니다.</div>;
};
export default MyComponent;
App 컴포넌트에서 name이라는 이름의 props 값을 지정하면 {props.name} 위치에 렌더링된다.
// App.js
const App = () => {
return <MyComponent name="React" />;
};
export default App;
defaultProps으로 props 값을 지정하지 않았을 때 보여줄 기본값을 설정할 수 있다.
// MyComponent.js
const MyComponent = props => {
return <div>안녕하세요, 제 이름은 {props.name}입니다.</div>;
};
MyComponent.defaultProps = {
name: '기본 이름'
};
// App.js
const App = () => {
return <MyComponent />;
};
export default App;
children은 컴포넌트 태그 사이의 내용을 보여주는 props이다.
// App.js
const App = () => {
return <MyComponent>리액트</MyComponent>;
};
export default App;
// MyComponent.js
const MyComponent = props => {
return (
<div>
안녕하세요, 제 이름은 {props.name}입니다. <br />
children 값은 {props.children}
입니다.
</div>
);
};
MyComponent.defaultProps = {
name: '기본 이름'
};
export default MyComponent;
MyComponent에서 props 값을 조회할 때마다 props 키워드를 붙여 사용하고 있다.
예) props.name, props.children
ES6의 비구조화 문법을 사용하면 객체에서 값을 추출할 수 있어 다음과 같이 간략하게 작성할 수 있다.
* 비구조화(구조 분해) 문법 ▶ https://corner-ds.tistory.com/24#Destructed_Assignment
// MyComponent.js
const MyComponent = props => {
const { name, children } = props;
return (
<div>
안녕하세요, 제 이름은 {name}입니다. <br />
children 값은 {children}
입니다.
</div>
);
};
MyComponent.defaultProps = {
name: '기본 이름'
};
export default MyComponent;
비구조화 문법은 구조 분해 문법이라고도 불리며, 함수의 매개변수 부분에서도 사용할 수 있다.
함수의 매개변수가 객체라면 그 값을 바로 비구조화해서 사용하는 것이다.
다음과 같이 수정할 수 있다.
// MyComponent.js
const MyComponent = ({ name, children }) => {
return (
<div>
안녕하세요, 제 이름은 {name}입니다. <br />
children 값은 {children}
입니다.
</div>
);
};
MyComponent.defaultProps = {
name: '기본 이름'
};
export default MyComponent;
컴포넌트의 필수 props를 지정하거나 props의 타입(자료형)을 지정할 때는 propTypes를 사용한다.
우선 코드 상단에 import 구문을 사용하여 불러와야 한다.
// MyComponent.js
import PropTypes from 'prop-types';
const MyComponent = ({ name, children }) => {
return (...);
};
Mycomponent.defaultProps = {
name: '기본 이름'
};
MyComponent.propTypes = {
name: PropTypes.string // name 값은 무조건 문자열(string) 형태로 전달
};
export default MyComponent;
만약 컴포넌트에 설정한 props가 propTypes에서 지정한 형태와 일치하지 않는다면,
웹페이지에 값이 출력되기는 하지만 브라우저 개발자 도구의 Console 탭에 다음과 같은 경고가 나타난다.
propType를 지정하지 않았을 때 경고 메시지를 띄우려면, 뒤에 .isRequired를 붙여 주면 된다.
favoriteNumber라는 숫자를 필수 props로 지정해보자.
// MyComponent.js
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
const import_Component = () => {
return <MyComponent name={'React'} favoriteNumber={'안녕'}>
리액트
</MyComponent>;
};
export default import_Component;
이때 favoriteNumber의 값을 지정하지 않으면 경고가 나타난다.
• 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인 객체
• shape({ name: PropTypes.string, num: PropTypes.number }): 주어진 스키마를 가진 객체
• any: 아무 종류
더 자세한 정보는 https://github.com/facebook/prop-types에서 확인할 수 있다.
src 디렉터리에 Counter.js 파일을 생성하여 다음 코드를 작성해보자.
// 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를 사용하여 state에 새로운 값을 넣을 수 있음
this.setState({ number: number + 1});
}}
>
+1
</button>
</div>
);
}
}
export default Counter;
위 파일에서 각 코드의 역할
// App.js
import Counter from './Counter';
const App = () => {
return <Counter />;
};
export default App;
state 안에 새 값을 설정했어도 this.setState 함수의 인자로 전달하지 않으면 함수 호출 시에도 값이 바뀌지 않는다.
// Counter.js
(...)
constructor(props){
super(props);
// state의 초기값 설정
this.state ={
number: 0,
fixedNumber: 0 // fixedNumber 추가
};
}
render() {
const { number, fixedNumber } = this.state; // fixedNumber 추가
(...)
<h2>바뀌지 않는 값: { fixedNumber } </h2>
<button
// onClick을 통해 버튼이 클릭되었을 때 호출할 함수 지정
onClick={ () => {
// this.setState를 사용하여 state에 새로운 값을 넣을 수 있음
// 이곳에 fixedNumber를 수정할 만한 명령이 없음
this.setState({ number: number + 1});
(...)
constructor 메서드(생성자)를 선언하는 방법 외에 state의 초기값을 지정하는 또 다른 방법
// Counter.js
(...)
class Counter extends Component {
state = { // 이것을 추가한다
number: 0,
fixedNumber: 0
};
render() {
const{ number, fixedNumber } = this.state;
return (...);
}
}
export default Counter;
this.setState를 사용하여 state에 새로운 값을 업데이트할 때는 상태가 비동기적으로 업데이트된다.
- 다음과 같이 onClick에 설정한 함수 내부에서 this.setState를 두 번 호출해도 값은 1만 더해진다.
- this.setState를 사용한다고 해서 state 값이 바로 바뀌지는 않기 때문이다.
// Counter.js - button onclick
onClick={() => {
// this.setState를 사용하여 state에 새로운 값을 넣을 수 있음
this.setState({ number: number+1 });
this.setState({ number: this.state.number +1 });
}}
해결책: this.setState를 사용할 때 객체 대신 함수를 인자로 넣기
※ 화살표 함수에서 값을 바로 반환하고 싶다면 코드 블록 { } 과 그에 포함된 return문을 생략
// Counter.js - button onclick
onClick={() => {
// this.setState를 사용하여 state에 새로운 값을 넣을 수 있음
this.setState(prevState => {
return {
number: prevState.number +1
};
});
// 위 코드와 아래 코드는 완전히 똑같은 기능
//아래 코드는 함수에서 바로 객체를 반환한다는 의미
this.setState(prevState => ({
number: prevState.number + 1
}));
}}
setState의 두 번째 매개변수로 콜백 함수를 등록하여 작업 처리
// Counter.js - button onclick
onClick={() => {
this.setState(
{ number: number + 1 },
() => {
console.log('방금 setState가 호출되었습니다.');
console.log(this.state);
}
);
}}
리액트 16.8 버전부터는 Hooks 기능 중 useState라는 함수를 사용하면, 함수 컴포넌트에서도 state를 이용할 수 있게 되었다.
- 배열 비구조화 할당: 객체 비구조화 할당과 비슷하게, 배열 안의 값을 추출하는 문법
아래 코드는 array 안의 값을 one과 two에 각각 담는다.
const array = [1,2];
const one = array[0];
const two = array[1];
// 위 코드를 배열 비구조화 할당을 사용하면 아래와 같이 표현 가능
const array = [1, 2];
const [one, two] = array;
src 디렉터리에 Say.js 파일을 새로 생성해 다음 코드를 작성
// Say.js
import { useState } from 'react';
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>
);
};
export default Say;
사용 형태
const [현재 상태, 세터 함수] = useState(현재 상태의 초기값);
세터 함수(현재 상태에 설정할 값);
여기서 현재 상태가 state의 역할을 하는 셈이다.
● useState 함수의 인자에는 상태의 초기값을 기술
- 값의 형태는 자유 (숫자, 문자열, 객체, 배열, 객체 등 모두 가능)
- cf. 클래스형 컴포넌트에서는 state의 초기값을 반드시 객체 형태로 지정
● useState 함수를 호출하면 배열 반환
- 첫 번째 원소 : 현재 상태
- 두 번째 원소 : 상태를 바꾸어 주는 함수(=세터 함수)
// App.js
import Say from './Say';
const App = () => {
return <Say />;
};
export default App;
여러 가지 상태(state)를 useState로 관리하는 코드를 작성해보자.
import { useState } from 'react';
const Say = () => {
const [message, setMessage] = useState('');
const onClickEnter = () => setMessage('안녕하세요!');
const onClickLeave = () => setMessage('안녕히 가세요!');
const [color, setColor] = useState('black'); // 새로 추가된 상태(state)
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>
);
};
export default Say;
// 클래스형 컴포넌트에서...
this.state.number = this.state.number + 1;
this.state.array = this.array.push(2);
this.state.object.value = 5;
// 함수 컴포넌트에서...
const [object, setObject] = useState({ a:1, b:1 });
object.b = 2;
// 객체 다루기
const object = { a: 1, b: 2, c: 3 };
const nextObject = { ...object, b: 2 ); // 사본을 만들어 b 값만 덮어 쓰기
// 배열 다루기
const array = [
{ id: 1, value: true },
{ id: 2, value: true },
{ id: 3, value: false }
};
let nextArray = array.concat({ id: 2}); // 새 항목 추가
nextArray.filter(item -> item.id !==2); // id가 2인 항목 제거
nextArray.map(item => (item.id === 1 ? { ...item, value: false } : item)); // id가 1인 항목의 value를 false로 설정
1. 클래스형 컴포넌트에서는 state 기능 및 라이프사이클 기능을 사용할 수 있고, (JSX)를 반환하는 (render) 함수를 반드시 필요로 한다.
2. props는 컴포넌트가 사용되는 과정에서 (컴포넌트 자신/부모 컴퍼넌트)가 설정하는 값이다.
3. 리액트의 state는 (클래스형) 컴포넌트가 지니고 있는 state, ( 함수 ) 컴포넌트에서 (useState)라는 함수를 통해 사용하는 state로 두 가지 종류가 있다.
4. 클래스형 컴포넌트에서 constructor 메서드는 (생성자)의 역할을 한다.
5. useState 함수 사용하는 방법
const [현재 상태, 세터 함수] = useState(현재 상태의 초기값);
세터 함수(현재 상태에 설정할 값);
6. state의 값을 변경하려면 직접 접근해서는 안 되고 (setState) 혹은 (useState)를 통해 전달받은 (세터 함수)를 사용해야 한다.
7. 객체에 대한 사본을 만들 때에는 (spread) 연산자라 불리는 (...)을 사용하여 처리하고,
배열에 대한 사본을 만들 때에는 배열의 내장 함수들을 활용한다.
1. (1) 함수 매개변수를 비구조화하고 * Hint: 3.5
(2) num 속성을 필수로 입력받게 하되 * Hint: 3.6
(3) 숫자 자료형이 입력되게 하기 * Hint: 3.6
// MyComponent.js
const MyComponent = props => {
const { num, children } = props;
return (
<div>
안녕하세요, 오늘 배울 내용은 {num}장입니다. <br />
부모 태그 안의 내용은 {children} 입니다.
</div>
);
};
export default MyComponent;
2. constructor 메서드를 선언하는 방법을 사용하지 않고 state의 초기값을 지정하도록 코드 변경하기 * Hint: 4.1.②
// Counter.js
import { Component } from 'react';
class Counter extends Component {
constructor(props){
super(props);
// state의 초기값 설정
this.state ={
number: 0
};
}
(...)
Corner React Starter #2
Editor 유즈
<리액트를 다루는 기술> 5장: ref: DOM에 이름 달기 (0) | 2021.11.22 |
---|---|
<리액트를 다루는 기술> 4장: 이벤트 핸들링 (0) | 2021.11.15 |
<리액트를 다루는 기술> 2장: JSX (0) | 2021.11.01 |
<리액트를 다루는 기술> 1장: 리액트 시작 (0) | 2021.11.01 |
[Codecademy] Learn JavaScript 8. Objects (0) | 2021.10.11 |