새싹 프론트엔드

새싹 프론트엔드 실무 과정 8주차 React Hooks

튼튼한간 2022. 12. 7. 11:25

새싹 프론트엔드 실무 과정 8주차 Hooks

 

 

  • 8주차 월요일

11. Hooks - 1

 

useState()

- 가장 기본적인 Hook

- 함수형 컴포넌트가 가변적인 상태를 지닐 수 있도록 해 줌

 

import React, { useState } from "react";

const AddName = () => {
  const [names, setNames] = useState(["간", "리액트"]);
  const [input, setInput] = useState("");

  function InputChange(e) {
    setInput(e.target.value);
  }
  function uploadInput() {
    setNames([input]);
  }

  return (
    <div>
      <input type="text" onChange={InputChange} />
      <button onClick={uploadInput}>추가</button>
      <div>
        {names.map((name, idx) => (
          <p key={idx}>{name}</p>
        ))}
      </div>
    </div>
  );
};

export default AddName;

 

기존에 있던 두 개의 값에 추가를 했으면 좋겠으니, 기존값 유지 + 새로운 값 추가

➡ setState()의 콜백 함수에 prevState 값을 전달해서 유지

 

function uploadInput() {
setNames((prevState) => [input, ...prevState]);
}

function uploadInput() {
    setNames((prevState) => [...prevState, input]);
  }

setState() 함수 동작 과정

 

useState() 성능 최적화

useState() 함수의 인자에 초기값을 지정한 경우

- state 값이 업데이트 될 때마다 초기값이 계속해서 호출됨

- 만약 초기값에 복잡한 계산식이 있다면 성능 저하 문제 발생

 

 

 

useEffect() 

- 리액트 컴포넌트가 렌더링 될 때마다 특정 작업을 수행하 도록 설정해주는 Hook

- 최초에 한번 실행하게 하고 싶은 작업을 작성할 때 주로 사용

 

Mount : 화면에 첫 렌더링

Updaste : 다시 렌더링

Unmount : 화면에서 제거

 

useEffect()가 받을 수 있는 데이터

매개변수1 : 콜백함수 (필수)

매개변수2 : 빈 배열 [ ]  (옵션)

매개변수3 : [value]

 

case 1)

- useEffect()의 매개변수에 콜백함수만 있는 경우 :

컴포넌트가 렌더링 될 때마다 콜백함수 실행 됨.

네트워크 통신을 통해 데이터를 가져올 때 매번 데이터를 가져옴

➡ 사용하는 의미❌

 

case 2)

- useEffect()의 매개변수에 콜백함수, 빈배열이 있는 경우 :

컴포넌트가 처음 렌더링 될 때 실행, value 값이 변경 되었을 때 실행

빈 배열이 있는 경우 최초에 한 번만 콜백함수를 실행

 

case 3) useEffect()의 매개변수에 콜백함수, 배열[value]이 있는 경우 :

- 1. 컴포넌트가 처음 렌더링 될 때

- 2. value값이 변경되었을 때 실행

 

 

import React, { useState } from "react";

const AddName = () => {
  const [names, setNames] = useState(() => heavyWork());
  const [input, setInput] = useState("");

  function InputChange(e) {
    setInput(e.target.value);
  }
  function uploadInput() {
    setNames((prevState) => [input, ...prevState]);
  }

  function heavyWork() {
    for (let i = 0; i < 1000; i++) {
      console.log("엄청 복잡한 계산 중.. 시간 오래 걸림..");
      return ["간", "리액트"];
    }
  }

  return (
    <div>
      <input type="text" onChange={InputChange} />
      <button onClick={uploadInput}>추가</button>
      <div>
        {names.map((name, idx) => (
          <p key={idx}>{name}</p>
        ))}
      </div>
    </div>
  );
};

export default AddName;

 

 
정말 엄청나다..
 
 
 
 
import React, { useState, useEffect } from "react";

const UseEffect = () => {
  const [names, setNames] = useState(() => heavyWork());
  const [input, setInput] = useState("");

  function InputChange(e) {
    setInput(e.target.value);
  }
  function uploadInput() {
    setNames((prevState) => [input, ...prevState]);
  }

  function heavyWork() {
    for (let i = 0; i < 1000; i++) {
      console.log("엄청 복잡한 계산 중.. 시간 오래 걸림..");
    }
    return ["간", "리액트"];
  }
  useEffect(() => {
    console.log("렌더링이 완료되었습니다.");
    console.log({ names });
  }, [names]);

  return (
    <div>
      <input type="text" onChange={InputChange} />
      <button onClick={uploadInput}>추가</button>
      <div>
        {names.map((name, idx) => (
          <p key={idx}>{name}</p>
        ))}
      </div>
    </div>
  );
};

export default UseEffect;

 

뒷정리하기 - cleanup

useEffect() 함수 내부에서 return 함수를 반환하면 됨

useEffect(() => {
    console.log("렌더링이 완료되었습니다.");
    console.log({ names });

    return () => {
      console.log("cleanup");
      console.log({ names });
    };
  }, [names]);

 

 

 

useRef()

- 컴포넌트 내부에서 사용되는 변수를 저장하는 Hook

 

특징

- 컴포넌트가 재렌더링되어도 저장된 변수 값을 유지

- 불필요한 렌더링을 방지할 수 있음

- 특정 DOM 요소에 접근 가능

 

import React, { useRef } from "react";

const UseRefComponent1 = () => {
  const ref = useRef("안녕하세요");
  console.log("변경 전 ref 값 : ", ref.current);

  ref.current = "Hello";
  console.log("변경 후 ref 값 : ", ref.current);
  return <div></div>;
};

export default UseRefComponent1;
 

 

State vs useRef()

useState() 렌더링과 관련 없는 값을 저장하기에는 적합치 않다.

useRef() 리랜더링을 하지 않음. 렌더링과 관련이 없는 값을 저장하기에 적합하다.

 

 

useRef() vs 일반 변수

일반변수는 렌더링시 값이 초기화되지만,
useRef의 값은 컴포넌트의 생애주기를 통해 유지가 되어 컴포넌트가 렌더링이 되어도 값을 유지한다.

 

✔ 화면이 unmount되면 ref값이 초기화

 

 

useRef()로 DOM에 접근하기

inputRef.current.focus();

 

깜빡깜빡

 

 

 

 

 

 

  • 8주차 화요일

12. Hooks - 2

 

useReducer()

 

재사용이 가능해짐

 

useReducer() vs useState()

➡ 사용하는 방법은 다르지만 결과는 같음!

 

useState()

- 컴포넌트에서 관리하는 값이 한 개

- 값이 단순한 숫자, 문자열, 불리언 등의 값인 경우

 

useReducer()

- 컴포넌트에서 관리하는 값이 여러 개

- 구조가 복잡한 경우

 

사용방법 :

const [state, dispatch] = useReducer(reducer, initialState);

dispatch() 사용 방법 : dispatch({ key : value })

ex) dispatch({메뉴 : 햄버거})➡ 액션 (객체)

 

 

일반적으로 따로 파일을 생성! 따로 파일을 빼놔야, 다른 파일에서 import해서 사용 가능

 

 

컴포넌트가 아니기 때문에, 첫 글자 대문자 안해도 됨!

 

 

 

countReducer.js

const countReducer = (state, action) => {

  switch (action.type) {
    case "INCREMENT":
      return action.icon;
    case "DECREMENT":
      return action.icon;
    default:
      return state;
  }
};

export default countReducer;
 
 
 
➡ Counter.js
function numUp() {
    dispatch({ type: "INCREMENT", icon: "🌷" });
  }

  function numDown() {
    dispatch({ type: "DECREMENT", icon: "🌼" });
  }
 
 
첫 글자 대문자! 컴포넌트는 return값이 있음!

➡ 여러개의 값을 넣을 수 있음

 

useReducer() vs useState()

useState()

- 컴포넌트에서 관리하는 값이 한 개

- 값이 단순한 숫자, 문자열, 불리언 등의 값인 경우

 

useReducer()

- 컴포넌트에서 관리하는 값이 여러 개

- 구조가 복잡한 경우

- 로직을 분리 가능, 재사용 가능

 

 

 

useMemo()

- 동일한 계산을 하는 함수를 포함하고 있는 컴포넌트가 반복적으로 렌더링이 될 때, 해당 함수의 값을 메모리에 저장해 놓고 재사용할 수 있게 함

 

메모이제이션(Memoization) : 전문용어

- 동일한 계산을 반복해야 할 때, 이전에 계산한 값을 메모리에 저장함으로써 동일한 계산의 반복 수행을 제거

- 장점 : 값을 재사용, 별도의 계산❌ ➡ 프로그램 실행 속도를 빠르게 하는 기술

 

useRef()랑 같은 구조

 

첫 번째 매개변수 : 콜백함수

- 메모이제이션 할 값을 계산해서 반환해주는 함수

 

두 번째 매개변수 : 의존성 배열

- 배열 안의 값이 업데이트 될 때만 콜백함수를 재호출

 

의존성 배열이 빈 배열인 경우

: 컴포넌트가 마운트되었을 때만 콜백함수 호출

 

의존성 배열이 없는 경우

: 컴포넌트가 렌더링 될 때마다 콜백함수 호출 ➡ useMemo() 사용하는 의미❌

 

const useMemoComponent = ({ a, b }) => {
  const result = useMemo(() => compute(a, b), [a, b]);
  return <div>{result}</div>;
};

➡ 최초 한번, a 또는 b의 값이 바뀔 때 실행

 

화면이 랜더링 될 때마다 계속 실행 중, 값을 저장

쉬운 계산만 눌렀는데, 둘다,,

 

const hardSum = useMemo(() => hardCalculate(hardNumber), [hardNumber]);

useMemo 사용 시 주의점

목적 : 값을 재사용하기 위해 별도의 메모리를 할당하여 값을 저장

➡ 불필요한 값까지 메모이제이션하면 메모리 용량이 늘어나 성능 저하 발생

 

 

 

useCallBack()

- 함수를 재 사용하기 위한 Hook

 

const UseCallBackComponent1 = () => {
  const name1 = () => "gan";
  const name2 = () => "gan";

  console.log("name1 : ", name1);
  console.log("name2 : ", name2);

  return <div>{name1 === name2 ? "같다" : "다르다"}</div>;
};

함수는 객체이기 때문에 주소를 가지고 있음.

===으로 비교하면 서로 다른 메모리에 저장되어 있었기 때문에 '다르다'

 

 

코드를 다시 실행할 때 마다 clickHandler을 다시 실행

 

 
useEffect(() => {
    console.log("clickHandler() 변경");
  }, [clickHandler]);
  // 하나 만들어 놓고, 함수 이름을 집어 넣기
  // 랜더링 후 함수가 변경되지 않았다면 새로 만들지마!
 
 
 
const [count, setCount] = useState(0);

  const clickHandler = useCallback(() => {
    console.log("count : ", count);
  }, []);
// 빈 배열[] : 최초 랜더링 됐을 때만
 
여전히 count : 0,,

useState(0)에서 0으로 저장했던 게, count에서 0으로 저장되어있음

count바뀔 때 만 변경해줘~
 
const clickHandler = useCallback(() => {
    console.log("count : ", count);
  }, [count]);

 

 

 

React.memo()

컴포넌트를 통채로 저장

 

- 리액트에서 제공하는 고차 컴포넌트

- props의 변화가 있는지를 체크

 

 

➡ App.js

import React, { useState } from "react";
import ChildComponent from "./2022-12-06/ChildComponent";

const App = () => {
  const [count, setCount] = useState(0);

  const updateHandler = () => {
    console.log("update");
  };

  return (
    <div>
      <input type="number" onChange={(e) => setCount(e.target.value)} />
      <ChildComponent update={updateHandler} />
    </div>
  );
};

export default App;
 
 
 
➡ ChildComponent.js
 
import React from "react";

const ChildComponent = (props) => {
  const { update } = props;
  console.log("child component 렌더링");

  return <div></div>;
};

export default ChildComponent;

부모가 랜더링 되는데 자식이 자꾸 실행 되는 이유는?

함께 랜더링 중..

 

 

const updateHandler = useCallback(() => {
    console.log("update");
  }, []);

 

자식도 부모로 부터 props 전달 받을 때 마다 매번 새로운 객체로 바뀜

 

props 내부의 값이 바뀌지 않았으면 랜더링 하지마!

 

 

➡ ChildComponent.js

컴포넌트를 통채로 메모리에 저장

 

 

3가지 메모

value를 메모리에 저장 : useCallback()

일반 함수 : useMemo()

컴포넌트를 저장리액트의 메모 함수 : React.memo()

 

 

 

 

새싹DT 기업연계형 프론트엔드 실무 프로젝트 과정 8주차 블로그 포스팅