애플리케이션의 사용자 인터페이스를 설계를 할 때 사용자가 볼 수 있는 요소들은 여러 컴포넌트로 구성되어 있다. 컴포넌트는 단순한 템플릿 이상의 기능을 갖추고 있다.
컴포넌트를 선언하는 방식에는 두가지가 있다. 함수 컴포넌트, 클래스형 컴포넌트이다.
함수 컴포넌트(2장에서 봤던 App 컴포넌트가 함수 컴포넌트임)
import './App.css';
function App(){
const name = '리액트';
return
{name}
;
}
export default App;
**클래스형 컴포넌트**
* state 및 라이프사이클 API 사용 가능
* 임의 메서드 정의 가능
* render 함수 반드시 필요(JSX 반환해주어야 함)
```javascript
import {Component} from 'react';
class App extends Component {
render() {
const name = 'react';
return <div className = "react">{name}</div>;
}
}
export default App;
// MyComponent.js
const MyComponet = () => {
return <div>나의 새롭고 멋진 컴포넌트</div>;
};
export default MyComponet;
코드가 어떤 의미를 가지고 있는지는 하나씩 배워가며 알아보자.
모듈 내보내기 (export)
방금 작성한 코드의 맨 아래 줄이 다른 파일에서 이 파일을 import할 때 불러올 수 있는 함수를 지정하는 코드이다.
export default MyComponet;
모듈 불러오기 (import)
App.js에서 MyComponent를 불러와서 사용하기 위한 코드
//App.js
import MyComponet from "./MyComponent";
const App = () => {
return <MyComponent />;
};
export default App;
props는 properties의 줄임말로, 컴포넌트 속성을 설정할 때 사용하는 요소이다. props 값은 해당 컴포넌트를 불러와 사용하는 부모 컴포넌트에서 설정할 수 있다.
MyComponent 컴포넌트를 수정하여 해당 컴포넌트에서 name이라는 props를 렌더링하도록 설정해보자.
// MyComponent.js
const MyComponent = props => {
return <div>안녕하세요, 제 이름은 {props.name}입니다.</div>;
};
export default MyComponent;
App 컴포넌트에서 MyComponent의 props 값을 지정해보자.
name이라는 이름의 props 값을 지정하면 {props.name} 위치에 렌더링된다.
// App.js
import MyComponet from "./MyComponent";
const App = () => {
return <MyComponent name="React" />;
};
export default App;
props 값을 따로 지정하지 않았을 때 보여줄 기본값을 defaultProps으로 설정할 수 있다.
// MyComponent.js
const MyComponent = props => {
return <div>안녕하세요, 제 이름은 {props.name}입니다.</div>;
};
MyComponent.defaultProps = {
name: '기본 이름'
};
export default MyComponent;
// App.js
import MyComponet from "./MyComponent";
const App = () => {
return <MyComponent />;
};
export default App;
// MyComponent.js
const MyComponent = props => {
return (
<div>
안녕하세요, 제 이름은 {props.name}입니다. <br />
children 값은 {props.children}
입니다.
</div>
);
};
MyComponent.defaultProps = {
name: '기본 이름'
};
export default MyComponent;
// App.js
import MyComponet from "./MyComponent";
const App = () => {
return <MyComponent>리액트</MyComponent>;
};
export default App;
앞에서 쓴 코드를 보면 MyComponent에서 props의 값을 조회할 때마다 props.name, props.children과 같이 props을 붙여주고 있다. 이러한 작업을 더 편하게 하기 위해 비구조화 할당 문법을 이용한다.
// 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;
//App.js
import MyComponet from "./MyComponent";
const App = () => {
return <MyComponet name={3}>리액트</MyComponet>;
};
export default App;
만약 컴포넌트에 설정한 props가 propTypes에서 지정한 형태와 일치하지 않는다면,
값이 나타나기는 하지만 콘솔에 경고 메시지를 출력하여 개발자에게 propTypes가 잘못되었다는 것을 알려줍니다.
propType를 지정하지 않았을 때 경고 메시지를 띄우려면, 뒤에 .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 MyComponet from "./MyComponent";
const App = () => {
return <MyComponet name={3}>리액트</MyComponet>;
};
export default App;
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에서 확인할 수 있다.
state는 컴포넌트 내부에서 바뀔 수 있는 값을 의미한다. 리액트에는 두 가지 종류의 state가 있다. 클래스형 컴포넌트가 지니고 있는 state이 있고, 함수 컴포넌트에서 useState 함수를 통해 사용할 수도 있다.
// Counter.js
import { Component } from 'react';
class Counter extends Component {
constructor(props){
super(props);
this.state ={ // 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;
위에 코드에 대해 알아보자.
constructor(props){
super(props);
this.state ={
number: 0
};
}
render() {
const { number } = this.state;
return (
<div>
<h1>{number}</h1>
<button
onClick={() => {
this.setState({ number: number + 1});
}}
>
+1
</button>
</div>
);
}
// App.js
import Counter from './Counter';
const App = () => {
return ;
};
export default App;
![](https://velog.velcdn.com/images/pyounani/post/26b1dc9a-e902-46fa-83b1-f0c9b24f80c3/image.jpg)
## 3-3-2. state 객체 안에 여러 값이 있을 때
state 객체 안에는 여러 값이 존재할 수 있다.
```javascript
// Counter.js
import { Component } from 'react';
class Counter extends Component {
constructor(props){
super(props);
this.state ={ // 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;
this.setState 함수의 인자로 전달하지 않으면 함수 호출 시에도 값이 바뀌지 않는다.
앞에서느 state 값을 초기화하기 위해 constructor 메서드(생성자)를 선언해주었는데 다른 방식으로도 초기값을 설정할 수 있다.
// Counter.js
import { Component } from 'react';
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
onClick={() => {
// this.setState를 사용하여 state에 새로운 값을 넣을 수 있음
this.setState({ number: number+1 });
this.setState({ number: this.state.number +1 });
}}
따라서 this.setState를 사용할 때 객체 대신 함수를 인자로 넣어주면 된다.
// Counter.js
onClick={() => {
// this.setState를 사용하여 state에 새로운 값을 넣을 수 있음
this.setState(prevState => {
return {
number: prevState.number +1
};
});
// 위 코드와 아래 코드는 완전히 똑같은 기능
//아래 코드는 함수에서 바로 객체를 반환한다는 의미
this.setState(prevState => ({
number: prevState.number + 1
}));
}}
setState의 두 번째 매개변수로 콜백 함수를 등록하여 작업 처리할 수 있다.
// Counter.js
onClick={() => {
this.setState(
{ number: number + 1 },
() => {
console.log('방금 setState가 호출되었습니다.');
console.log(this.state);
}
);
}}
리액트 16.8 버전부터는 Hooks 기능 중 useState라는 함수를 사용하면, 함수 컴포넌트에서도 state를 이용할 수 있게 되었다.
객체 비구조화 할당과 비슷하게, 배열 안의 값을 추출하는 문법이다.
const array = [1,2];
const one = array[0];
const two = array[1];
// 위 코드를 배열 비구조화 할당을 사용
const array = [1, 2];
const [one, two] = array;
// 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;
위의 코드에 대해 알아보자.
// App.js
import Say from './Say';
const App = () => {
return <Say />;
};
export default App;
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;
state의 값을 변경하려면 상태(state)에 직접 접근하는 것이 아니라
setState 혹은 useState를 통해 전달받은 세터 함수를 사용해야 한다.
// 클래스형 컴포넌트
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;
배열이나 객체를 업데이트할 때에는 배열이나 객체의 사본을 만들어 업데이트할 값을 저장한 후,
그 사본을 setState 혹은 세터 함수를 통해 업데이트해야 한다.
이에 대한 자세한 내용은 이후에 배워 보겠다.
// 객체 다루기
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로 설정
◎리미
[리액트스타터2] 5장. ref (0) | 2022.11.05 |
---|---|
[리액트스타터2] 4장. 이벤트 핸들링 (0) | 2022.10.13 |
[리액트스타터2] 2장. JSX (1) | 2022.09.29 |
[리액트스타터2] 1장. 리액트 시작 (1) | 2022.09.29 |
[Javascript] 08. Objects (0) | 2022.09.22 |