---
# System prepended metadata

title: 리엑트를 다루는 기술 6장 정리

---

---
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값이 실제 엘리먼트를 가리킴