TIL 22-04-12 (2)

thisisyjin·2022년 4월 12일
0

TIL 📝

목록 보기
21/113

React.js

Today I Learned ... react.js

🙋‍♂️ React.js Lecture

🙋‍ My Dev Blog


React Lecture CH 5

1 - 라이프사이클
2 - setInterval과 라이프사이클 연동
3 - 가위바위보 게임
4 - 고차함수 (Higher-order Func)

5 - useEffect
6 - 클래스 vs Hooks 라이프사이클 비교

useEffect

❗️ 주의

  • 클래스에서 Hooks로 변경할 때,
    생명주기 함수(life cycle)는 Hooks에 존재하지 않음.
// Hooks

import React, { useState, useRef, useEffect } from 'react';

const rspCoords = {
  바위: '0',
  가위: '-142px',: '-284px',
};

const scores = {
  가위: 1,
  바위: 0,: -1,
};

const computerChoice = (imgCoord) => {
  return Object.entries(rspCoords).find(function (v) {
    return v[1] === imgCoord;
  })[0];
};

const RSP = () => {
  const [result, setResult] = useState('');
  const [imgCoord, setImgCoord] = useState(rspCoords.바위);
  const [score, setScore] = useState(0);

  const interval = useRef();

  // ✅ componentDidMount, componentDidUpdate의 역할
  useEffect(() => {
    interval.current = setInterval(changeHand, 100);
    return () => {
      // componentWillUnmount 역할
      clearInterval(interval.current);
    };
  }, [imgCoord]); // useEffect를 실행하고 싶은 (바뀌는) state를 적어줌. (리액트가 지켜볼 state.)
  // ❗️ 빈 배열이면 - 맨 처음 딱 한번만 실행됨.

  const changeHand = () => {
    if (imgCoord === rspCoords.바위) {
      setImgCoord(rspCoords.가위);
    } else if (imgCoord === rspCoords.가위) {
      setImgCoord(rspCoords.);
    } else if (imgCoord === rspCoords.) {
      setImgCoord(rspCoords.바위);
    }
  };

  const onClickBtn = (choice) => () => {
    clearInterval(interval.current);
    const myScore = scores[choice];
    const cpuScore = scores[computerChoice(imgCoord)];
    const diff = myScore - cpuScore;
    if (diff === 0) {
      setResult('비겼습니다.');
    } else if ([-1, 2].includes(diff)) {
      setResult('이겼습니다!');
      setScore((prevScore) => prevScore + 1);
    } else {
      setResult('졌습니다!');
      setScore((prevScore) => prevScore - 1);
    }
    setTimeout(() => {
      interval.current = setInterval(changeHand, 100);
    }, 1000);
  };

  return (
    <>
      <div
        id="computer"
        style={{
          background: `url(https://en.pimg.jp/023/182/267/1/23182267.jpg) ${imgCoord} 0`,
        }}
      />
      <div>
        <button id="rock" className="btn" onClick={onClickBtn('바위')}>
          바위
        </button>
        <button id="scissor" className="btn" onClick={onClickBtn('가위')}>
          가위
        </button>
        <button id="paper" className="btn" onClick={onClickBtn('보')}></button>
      </div>
      <div>{result}</div>
      <div>현재 {score}</div>
    </>
  );
};

export default RSP;

useState

  • state별로 useState[0], useState[1]로 나누어 설정.
const [result, setResult] = useState('');

useEffect

  • Hooks에는 클래스 컴포넌트와 달리 LifeCycle Method가 없다.
  • 대신, 비슷한 기능을 하는 useEffect를 사용할 수 있다.
  • useEffect는 componentDidMount + componentDidUpdate의 역할을 한다.
  • 단, 1:1로 대응하지는 않는다.
  • useEffect의 첫번째 인수로는 콜백이 들어가고, 두번째 인수로는 deps(dependencies) 배열이 들어간다.
    = useEffect의 콜백을 다시 실행할 state만 적기.
  • deps가 빈 배열이면 최초 1회만 실행되고,(componentDidMount)
  • 안에 state를 적어주면, 해당 state를 지켜보게 된다.
    (그 state가 업데이트되면 다시 실행됨)
  • 콜백 내에 return() {} 으로 componentWillUnmount 역할을 할 수 있다.

+) 참고로, Hooks가 나오기 전에는 recompose 라이브러리의 lifecycle 메서드로 '함수형'에서도 생명주기 함수를 사용할 수 있었다.


React 공식 문서 살펴보기

  • React의 class 생명주기 메서드에 친숙하다면, useEffect Hook을 componentDidMount와 componentDidUpdate, componentWillUnmount가 합쳐진 것으로 생각해도 좋다.

side effects

1. 정리(Clean-up)를 이용하지 않는 Effects

React가 DOM을 업데이트한 뒤 추가로 코드를 실행해야 하는 경우가 있습니다.
네트워크 리퀘스트, DOM 수동 조작, 로깅 등은 정리(clean-up)가 필요 없는 경우들입니다.
이러한 예들은 실행 이후 신경 쓸 것이 없기 때문입니다.

  • 1️⃣ class 사용시

class 컴포넌트에서 render 메서드 그 자체는 side effect를 발생시키지 않습니다.
이때는 아직 이른 시기로서 이러한 effect를 수행하는 것은 React가 DOM을 업데이트하고 난 이후입니다.
(componentDidMount, componentDidUpdate)

  • 2️⃣ Hooks 사용시

렌더링 이후에 어떤 일을 수행해야 하는지.

  • 리액트는 우리가 넘긴 함수 (effect)를 기억했다가 DOM 업데이트 이후에 불러냄.

  • useEffect를 컴포넌트 내(함수형 컴포넌트)에 둠으로써
    effect를 통해 state 변수에도 접근할 수 있게 됨.
    -> 함수 범위 안에 존재하므로 값을 얻을 수 있음.

  • useEffect는 기본적으로 첫 렌더링 & 이후 모든 업데이트마다 매번 실행됨.
    -> 즉, 렌더링 이후에 매번 발생하는 것.


2. 정리(clean-up)를 이용하는 Effects

  • 예> 외부 데이터에 구독(subscription)을 설정해야 하는 경우를 생각해보겠습니다.
    -> 이런 경우에 메모리 누수가 발생하지 않도록 정리(clean-up)하는 것은 매우 중요합니다.
  • 1️⃣ class 사용시

    componentDidMount에 구독 설정 후,
    componentWillUnmount에서 정리.

  • 2️⃣ Hooks 사용시

구독(subscription)의 추가와 제거를 위한 코드는 결합도가 높기 때문에 useEffect는 이를 함께 다루도록 고안되었습니다.
effect가 함수를 반환하면 React는 그 함수를 정리가 필요한 때에 실행시킬 것입니다.

🙋‍♀️ effect에서 함수를 반환하는 이유는 무엇일까요?

  • 이는 effect를 위한 추가적인 정리(clean-up) 메커니즘입니다. 모든 effect는 정리를 위한 함수를 반환할 수 있습니다.

🙋‍♀️ React가 effect를 정리(clean-up)하는 시점은 정확히 언제일까요?

  • React는 컴포넌트가 마운트 해제되는 때에 정리(clean-up)를 실행합니다. 하지만 위의 예시에서 보았듯이 effect는 한번이 아니라 렌더링이 실행되는 때마다 실행됩니다. React가 다음 차례의 effect를 실행하기 전에 이전의 렌더링에서 파생된 effect 또한 정리하는 이유가 바로 이 때문입니다.

dependencies

  • 특정 값들이 리렌더링 시에 변경되지 않는다면 React로 하여금 effect를 건너뛰도록 할 수 있습니다.
  • useEffect의 선택적 인수인 두 번째 인수로 배열을 넘기면 됩니다.
    -> 마치 componentDidUpdate에서 prevProps나 prevState와 비교하는 것 처럼.
  • 배열 내에 여러 개의 값이 있다면 그중의 단 하나만 다를지라도 React는 effect를 재실행

요약

  • useEffect가 컴포넌트의 렌더링 이후에 다양한 side effects를 표현할 수 있음.
  • effect에 정리(clean-up)가 필요한 경우에는 함수를 반환
profile
기억은 한계가 있지만, 기록은 한계가 없다.

0개의 댓글