해당 게시물은 인프런의 Velopert님의 유료 강의를 듣고 개인적으로 정리한 내용을 담고 있습니다👩💻
데이터를 추가해보자
App 컴포넌트 state에 information이라는 배열을 만들고, 그 안에 샘플 데이터 두 개를 추가할 것이다. 각 전화번호는 다음과 같은 형식으로 담긴다.
{
id: 0,
name: '이름',
phone: '010-0000-0000'
}
여기서 id는 각 데이터를 식별하기 위해 사용되는 고유값이다. 수정 및 삭제 기능을 구현할 때 필요하다. 그리고 id 값은 데이터를 추가할 때마다 증가해야 하기 때문에 +1씩 더해줄 것이다.
// src/App.js
import React, { Component } from 'react';
import PhoneForm from './components/PhoneForm';
class App extends Component {
id = 2
state = {
information: [
{
id: 0,
name: '김민준',
phone: '010-0000-0000'
},
{
id: 1,
name: '홍길동',
phone: '010-0000-0001'
}
]
}
handleCreate = (data) => {
const { information } = this.state;
this.setState({
information: information.concat({ id: this.id++, ...data })
})
}
render() {
const { information } = this.state;
return (
<div>
<PhoneForm
onCreate={this.handleCreate}
/>
{JSON.stringify(information)}
</div>
);
}
}
export default App;
id 값의 경우, App 컴포넌트의 일반 클래스 내부 변수로 선언했다. 컴포넌트 내부에서 필요한 값들 중 렌더링 되는 것과 상관없는 값들은 굳이 state에 넣어줄 필요가 없기 때문이다. render 함수에서는 information 값을 문자열로 변환하여 보이도록 했다. (이후에 컴포넌트 형태로 렌더링 할 것이다.)
결과는 다음과 같다.
데이터 렌더링
이제 위 배열을 컴포넌트로 변환해보겠다. 여러 개의 컴포넌트를 렌더링하기 위해서는 자바스크립트 배열의 내장 함수인 map을 사용하면 된다.
+ map 함수 알아보기
예를 들어 다음과 같은 배열이 있다고 가정해보자
const a = [1,2,3,4,5];
배열 a의 원소들에 2를 곱한 값을 출력하고 싶다면 어떻게 해야할까
const a = [1,2,3,4,5];
const b = [];
a.forEach(number => b.push(number * 2));
위와 같이 forEach를 사용해서 구현할 수 있지만, map을 사용하면 조금 더 쉽게 해결할 수 있다.
const a = [1,2,3,4,5];
const b = a.map(number => number * 2);
정보 표시 컴포넌트 만들기
각 전화번호의 정보를 보여주는 컴포넌트인 PhoneInfo, 여러 개의 PhoneInfo 를 보여주는 컴포넌트인 PhoneInfoList를 생성해보자.
// src/components/PhoneInfo.js
import React, { Component } from 'react';
class PhoneInfo extends Component {
static defaultProps = {
info: {
name: '이름',
phone: '010-0000-0000',
id: 0
}
}
render() {
const style = {
border: '1px solid black',
padding: '8px',
margin: '8px'
};
const {
name, phone, id
} = this.props.info;
return (
<div style={style}>
<div><b>{name}</b></div>
<div>{phone}</div>
</div>
);
}
}
export default PhoneInfo;
이름과 전화번호 값이 담긴 info라는 객체를 props로 받아와서 렌더링해줄 것이다. 그런데 실수로 info 값을 전달해주는 것을 잊어버리게 된다면 컴포넌트가 크래시될 것이다. info가 undefined 일 때는 비구조화 할당을 통해 내부의 값을 받아올 수 없기 때문이다. 따라서 defaultProps를 통하여 info의 기본값을 설정해주었고, 크래시를 예방할 수 있다.
그 다음에는 PhoneInfoList 컴포넌트를 만들어 볼 것이다.
// src/components/PhoneInfoList.js
import React, { Component } from 'react';
import PhoneInfo from './PhoneInfo';
class PhoneInfoList extends Component {
static defaultProps = {
data: []
}
render() {
const { data } = this.props;
const list = data.map(
info => (<PhoneInfo key={info.id} info={info}/>)
);
return (
<div>
{list}
</div>
);
}
}
export default PhoneInfoList;
이 컴포넌트에서는 data라는 배열을 가져와 map을 통하여 JSX로 변환해준다. 이 과정에서 key라는 값도 설정되었는데, 여기서 key는 리액트에서 배열을 렌더링할 때 꼭 필요한 값이다. 리액트는 배열을 렌더링할 때 값을 통하여 업데이트 성능을 최적화하는데 다음 예를 살펴보자.
<div>A</div>
<div>B</div>
<div>C</div>
<div>D</div>
만약에 key를 부여하지 않으면, 배열의 index값이 자동으로 key로 설정되는데, key가 배열의 인덱스로 설정되어있는 상태는 이러하다.
<div key={0}>A</div>
<div key={1}>B</div>
<div key={2}>C</div>
<div key={3}>D</div>
여기서 B와 C 사이에 X를 집어넣는다고 가정해보면
<div key={0}>A</div>
<div key={1}>B</div>
<div key={2}>X</div> [C -> X]
<div key={3}>C</div> [D -> C]
<div key={4}>D</div> [새로 생성됨]
굉장히 비효율적으로 만들어진다. 사실상 중간에 끼워넣기만 하면 되는 것인데, 배열의 index를 key로 사용하게 되어 중간에 값이 들어가면 index도 함께 바뀌어버리기 떄문에 X 아래로 값이 다 바뀌게 되는 것이다.
key를 배열의 index 값으로 사용하는 것이 아니라, 데이터를 추가할 때마다 고정적인 고유 값을 부여해주면 리액트가 변화를 감지해내고 업데이트를 하게 될 때 조금 더 똑똑하게 처리할 수 있게 된다.
<div key={0}>A</div>
<div key={1}>B</div>
<div key={2}>C</div>
<div key={3}>D</div>
이번에 다시 B와 C 사이에 다시 X를 넣어보겠다. 이번에 key 값은 고정된 고유값(id)이다.
<div key={0}>A</div>
<div key={1}>B</div>
<div key={5}>X</div> [새로 생성됨]
<div key={2}>C</div> [유지됨]
<div key={3}>D</div> [유지됨]
결국 새로운 DOM은 하나만 생성되고, 나머지는 그대로 유지된다. key값은 언제나 고유한 값으로 유지해야한다. 실제 프로젝트를 예로들자면, DB에 데이터를 추가하면 주로 해당 데이터를 가리키는 고유 id가 있다. 그러한 데이터를 리액트에서 렌더링하게 된다면 그 고유 id를 가지고 key로 사용하면 된다. 지금 경우네는 전화번호 정보에서 id값을 key값을 사용해줬다.
이제 PhoneInfoList 컴포넌트를 App에서 렌더링하고, data값을 props로 전달해보자
// src/App.js
import React, { Component } from 'react';
import PhoneForm from './components/PhoneForm';
import PhoneInfoList from './components/PhoneInfoList';
class App extends Component {
id = 2
state = {
information: [
{
id: 0,
name: '김민준',
phone: '010-0000-0000'
},
{
id: 1,
name: '홍길동',
phone: '010-0000-0001'
}
]
}
handleCreate = (data) => {
const { information } = this.state;
this.setState({
information: information.concat({ id: this.id++, ...data })
})
}
render() {
return (
<div>
<PhoneForm
onCreate={this.handleCreate}
/>
<PhoneInfoList data={this.state.information}/>
</div>
);
}
}
export default App;
결과는 다음과 같다.
가끔씩은 데이터에 고유한 값이 없을 수도 있다. key값이 없는 상태로 렌더링해도 동작은 되지만, 개발자도구 콘솔에서 경고창이 뜨게된다. 그 경고가 보고싶지 않다면
const list = data.map(
(info, index) => (<PhoneInfo key={index} info={info}/>)
);
과 같이 작업하면 되나, 이것은 단순히 경고만 감출 뿐이다.
'React' 카테고리의 다른 글
[React] 클래스형 컴포넌트와 함수형 컴포넌트를 알아보자 - (1) props, state (4) | 2021.05.06 |
---|---|
리액트(ReactJS) 전화번호부 만들기 (3) (0) | 2021.05.04 |
리액트(ReactJS) 전화번호부 만들기 (1) (0) | 2021.05.04 |
리액트(ReacJS)의 컴포넌트 라이프사이클API - (3) Unmount API, componentDidCatch를 통한 에러잡기 (0) | 2021.04.30 |