---
layout: post
title: "리엑트를 다루는 기술 6장 정리"
date: 2019-10-24 10:43:02 +0900
---
# 6. 컴포넌트 반복
## 6.1 자바스크립트 배열의 `map()` 함수
```jsx=
import React from 'react';
const IterationSample = () => {
const names = ['눈사람', '얼음', '눈', '바람'];
const nameList = names.map((name, index)=><li key={index}>{name}</li>);
return <ul>{nameList}</ul>
};
export default IterationSample;
```
- `map()`함수를 이용하여 **반복되는 컴포넌트를 렌더링**할 수 있음
## 6.2 key
- 컴포넌트 배열을 렌더링했을 때 **어떤 원소에 변동이 있었는지** 알아내기 위해 `key`를 사용
- `key`가 없으면 Virtual DOM을 비교하는 과정에서 리스트를 순차적으로 비교함
- `key`가 있으면 어떤 변화가 일어났는지 **더욱 빠르게** 알 수 있음
- `key` 값은 항상 유일
- 상태 안에서 반복되는 배열을 수정할 경우에는 `concat`, `filter` 등의 함수를 사용하여 불변성을 유지해야 한다.
---
# 7. 컴포넌트의 라이프사이클 메서드
## 7.1 라이프사이클 메서드의 이해
- **클래스형 컴포넌트**에서만 `라이프사이클 메서드` 사용가능
- **함수형 컴포넌트**에서는 **`Hooks` 기능을 사용하여 비슷한 작업을 처리**
- 라이프사이클 메서드는 총 **세 가지**
- <mark>마운트</mark> : DOM이 생성되고 웹 브라우저상에 나타나는 것
- **`constructor`**
- **`getDerivedStateFromProps`**
- **`render`**
- **`componentDidMount`**
- <mark>업데이트</mark> : props, state가 변경되는 경우되는 경우 발생
- **`getDerivedStateFromProps`**
- **`shouldComponentUpdate`**
- **`render`**
- **`getSnapshotBeforeUpdate`**
- **`componentDidUpdate`**
- <mark>언마운트</mark> : 마운트의 반대 과정, DOM에서 컴포넌트를 제거
- **`componentWillUnmount`**
## 7.2 라이프사이클 메서드 살펴보기
- `render()` 메서드
- 라이프사이클 메서드 중 유일한 **필수 메서드**
- 이 메서드 안에서 `this.props`, `this.state`에 접근 가능
- **리액트 요소를 리턴**
- `counstructor` 메서드
- 컴포넌트의 생성자 메서드로 컴포넌트를 만들 때 처음 생성
- 초기 `state`를 설정
- `getDerivedStateFromProps` 메서드
- `props`로 받아 온 값을 `state`에 동기화시키는 용도
- 컴포넌트가 **마운트**, **업데이트**될 때 호출
- `componentDidMount` 메서드
- 컴포넌트를 만들고, 첫 렌더링을 마친 후 실행
- 프레임워크 함수 호출, 이벤트 등록, 비동기 작업을 처리할 때 사용
- `shouldComponentUpdate` 메서드
- props, state를 변경했을 때, **리렌더링을 할지 여부를 정하는 메서드**
- `true`를 리턴하면 리렌더링 진행, `false`를 리턴하면 리렌더링 중지
- `getSnapshotBeforeUpdate` 메서드
- `render`에서 만들어진 결과물이 브라우저에 실제 반영되기 직전에 호출
- `componentDidUpdate` 메서드
- 리렌더링을 완료 후 실행
- 업데이트가 끝난 후이므로 DOM 관련 처리 가능
- `componentWillUnmount` 메서드
- 컴포넌트를 DOM에서 제거할 때 실행
- `componentDidCatch` 메서드
- 컴포넌트 렌더링 도중에 에러를 처리하는 메서드
- 컴포넌트 자신에게 발생하는 에러는 처리할 수 없음
- 자신의 `this.props.children`으로 전달되는 컴포넌트에서 발생하는 에러만 처리 가능
---
# 8. Hooks
## 8.1 useState
```jsx=
import React, { useState } from 'react';
const Counter = () => {
const [value, setValue] = useState(0);
return (
<div>
<p>
헌재 카운터 값은 <b>{value}</b>입니다.
</p>
<button onClick={() => setValue(value + 1)}>+1</button>
<button onClick={() => setValue(value - 1)}>-1</button>
</div>
);
};
export default Counter;
```
- `useState`는 가장 기본적인 Hook
- **함수형 컴포넌트**에서도 가변적인 상태를 지닐 수 있도록 함
- `useState` 함수의 파라미터에는 **상태의 기본값**을 넣는다.
- `useState` 함수는 **배열을 리턴**
- 리턴된 배열의 첫 번째 원소는 **상태 값**, 두 번째 원소는 **상태를 설정하는 함수**
## 8.2 useEffect
```jsx=
import React, { useState, useEffect } from 'react';
const Info = () => {
const [name, setName] = useState('');
const [nickname, setNickname] = useState('');
useEffect(()=>{
console.log('렌더링이 완료되었습니다!');
console.log({
name,
nickname
});
});
const onChangeName = e => {
setName(e.target.value);
};
const onChengeNickname = e => {
setNickname(e.target.value);
};
return (
(...)
);
};
```
- `useEffect`는 컴포넌트가 **렌더링될 때마다 특정 작업을 수행**하도록 설정하는 `Hook`
- **클래스형 컴포넌트**의 `componentDidMount`, `componentDidUpdate`를 합친 형태
- **마운트될 때만 실행**하고 싶은 경우
```jsx=
useEffect(() => {}, []);
```
- useEffect 함수의 **두 번째 파라미터로 비어 있는 배열**을 입력
- **특정값이 업데이트될 때만 실행**하고 싶은 경우
```jsx=
useEffect(() => {}, [name]);
```
- useEffect 함수의 **두 번째 파라미터로 특정값**을 입력
- 뒷정리 하기
- 컴포넌트가 언마운트되기 전이나 업데이트되기 직전에 어떠한 작업을 수행하고 싶다면 useEffect에서 뒷정리(cleanup) 함수를 반환한다.
## 8.3 useReducer
- useState보다 더 다양한 컴포넌트 상황에 따라 상태값을 업데이트하도록 하는 `Hook`
- `reducer`는**현재 상태**, 업데이트를 위해 필요한 정보를 담은 **액션** 값을 전달받아 <mark>새로운 상태를 반환하는 함수</mark>
```jsx=
import React, { useReducer } from 'react';
function reducer(stat, action){
switch (action.type){
case 'INCREMENT':
return { value: state.value + 1 };
case 'DECREMENT':
return { value: state.value - 1 };
default:
return state;
}
}
const Counter = () => {
const [state, dispatch] = useReducer(reducer, { value: 0 });
return (
<div>
<p>
헌재 카운터 값은 <b>{state.value}</b>입니다.
</p>
<button onClick={() => dispatch({ type: 'INCREMENT' })}>+1</button>
<button onClick={() => dispatch({ type: 'DECREMENT' })}>-1</button>
</div>
);
};
export default Counter;
```
- `useReducer`의 **첫 번째 파라미터**에는 `reducer` 함수를 입력, **두 번째 파라미터**에는 해당 `reducer`의 기본값을 입력
- `useReducer` 함수는 배열을 리턴
- 리턴되는 **첫 번째 원소는 현재 상태**이고 **두 번째 원소는 액션을 발생시키는 함수**
- `useReducer`를 사용하는 가장 큰 장점은 컴포넌트 **업데이트 로직을 컴포넌트 바깥으로 빼낼 수 있다는 점**
## 8.4 useMemo
- `useMemo`를 사용하여 함수형 컴포넌트 내부에서 발생하는 연산을 최적화할 수 있음
- 렌더링하는 과정에서 특정 값이 바뀌었을 때만 연산을 실행하고, 값이 바뀌지 않았다면 이전에 연산했던 결과를 다시 사용하는 방삭
```jsx=
const avg = useMemo(() => getAverage(list), [list]);
```
## 8.5 useCallback
```jsx=
import React, { useState, useMemo, useCallback } from "react";
const getAverage = numbers => {
console.log("평균값 계산중...");
if (numbers.length === 0) return 0;
const sum = numbers.reduce((a, b) => a + b);
return sum / numbers.length;
};
const Average = () => {
const [list, setList] = useState([]);
const [number, setNumber] = useState("");
const onChange = useCallback(e=> {
setNumber(e.target.value);
}, []);
const onInsert = useCallback(e=> {
const nextList = list.concat(parseInt(number));
setList(nextList);
setNumber('');
}, [number, list]);
const avg = useMemo(() => getAverage(list), [list]);
return (
<div>
<input value={number} onChange={onChange}></input>
<button onClick={onInsert}>등록</button>
<ul>
{list.map((value, index) => (
<li key={index}>{value}</li>
))}
</ul>
<div>
<b>평균값:</b> {avg}
</div>
</div>
);
};
export default Average;
```
- `useCallback`은 `useMemo`와 상당히 비슷한 기능
- `useCallback`을 사용하면 **이벤트 핸들러 함수를 필요할 때만 생성 가능
- `useCallback`의 첫 번째 파라미터에는 생성하고 싶은 함수를 입력
- `useCallback`의 두 번째 파라미터에는 특정 값이 바뀌었을 때 함수를 새로 생성할지를 입력
## 8.6 useRef
- `useRef`는 함수형 컴포넌트에서 ref를 쉽게 사용할 수 있도록 함
- `useRef`를 사용하여 ref를 설정하면 `useRef`를 통해 만든 객체 안의 current값이 실제 엘리먼트를 가리킴