WEB/Front-end

React_useEffect, useReducer, 최적화 (React.js)

최새벽 2024. 7. 11. 17:13



#01. 리액트 컴포넌트 라이프 사이클

 

개발자 도구 - Profiler에서 라이프사이클 확인할 수 있음

 

- (생성) 마운트: 컴포너트를 페이지에 처음 랜더링 할 때

- 업데이트: State/Props의 값이 바뀌거나 부모 컴포넌트가 리랜더되어 본인도 리랜더 될 때

- (소멸)언마운트: 더 이상 페이지에 컴포넌트를 랜더링하지 않을 때. 사라짐.

 

[useEffect] 

값의 변경이 생길 때 마다 특정 코드를 실행하는 리액트 훅

  // useEffect(함수, 의존성배열);
  // 배열안에 들어간 변수가 바뀌면 앞의 함수가 실행
  useEffect(() => {
    console.log("count 업데이트:", count);
    console.log("text 업데이트:", text);
  }, [count, text]);

 

- 의존성 배열을 안넣을 경우: 변화가 일어날 때마다 useEffect실행

  useEffect(() => {
    console.log("컴포넌트 업데이트");
  });

 

- 마운트 시점과 업데이트 시점을 다르게 제어

  const didMountRef = useRef(false);
    if (!didMountRef.current) {
      didMountRef.current = true;
      return;
    } else {
      console.log("컴포넌트 업데이트");
    }
  });

  useEffect(() => {
    console.log("컴포넌트 마운트");
  }, []); // 빈 의존성 배열 : 의존성의 값을 안주겠다. ==> 맨 처음 한번만 실행

 

- setInterval을 통한 setEffect 제어

: setInterval의 콜백함수가 1초에 한번 콘솔을 찍는 함수 실행

: 다른 이벤트 일어나면 함수가 중첩돼서 동시에 스레드처럼 여러번 출력됨

     => 빠르게 출력 여러번 되는 것 처럼 보임

  useEffect(() => {
    // useEffect의 콜백 함수가 setInterval 함수 실행
    // (274p참고)
     setInterval(() => {
      console.log("깜빡");
    }, 1000);
  });

 

- 함수 변수를 선언하여 초기화하기

: intervalID를 선언하여 이벤트를 통한 interval함수가 중첩되지 않게 방지, 새 인터벌을 생성하여 기존 인터벌을 삭제하는 것

  useEffect(() => {
  const intervalID = setInterval(() => {
      console.log("깜빡");
    }, 1000);
    return () => {
      console.log('클린업');
      clearInterval(intervalID);
    }
  });

 

- 언마운트 제어하기

return (
    <div className="App">
      <h1>Simple Counter</h1>
      <section>
        <input value={text} onChange={handleOnChanageText} />
      </section>
      <section>
        <Viewer count={count} />
        {/* Even은 그냥 컴포넌트. 아래와 같이 하면 왼쪽 조건이 충족될 때 오른쪽 컴포넌트가 실행됨 */}
        {/* And 연산은 왼쪽 조건이 false일 때 오른쪽을 실행하지 않는다는 특성을 활용 */}
        {count % 2 === 0 && <Even />}
      </section>
      <section>
        <Controller handleSetCount={handleSetCount} />
      </section>
    </div>
  );

 

import { useEffect } from "react";

// 바로 export할 수 있음
export default () => {
  useEffect(() => {
    return () => {
      console.log("Even 컴포넌트 언마운트");
    };
  }, []);
  return <div>현재 카운트는 짝수입니다.</div>;
};

 

 

#02. useReducer

 

[useReducer]

import { useReducer } from "react";

function reducer(state, action) {
  switch (action.type) {
    case "INCREASE":
      return state + action.data;
    case "DECREASE":
      return state - action.data;
    default:
      return state;
  }
}

function TestComp() {
  const [count, dispatch] = useReducer(reducer, 0);

  return (
    <div>
      <h4>테스트 컴포넌트</h4>
      <div>
        <bold>{count}</bold>
      </div>
      <div>
        <button onClick={() => dispatch({ type: "INCREASE", data: 1 })}>
          +
        </button>
        <button onClick={() => dispatch({ type: "DECREASE", data: 1 })}>
          -
        </button>
      </div>
    </div>
  );
}

export default TestComp;

- count: state 변수

- dispatch: 상태 변화 촉발 함수, 함수가 호출되면 인수로 객체를 전달(상태 변화 함수로 객체 전달해서 실행시키는 역할)

- useReducer(상태 변화 함수, 초깃값)

- 위의 방식을 사용하면 비슷한 결의 새로운 기능을 추가할 때 함수를 새로 만드는 것이 아니라 case를 추가하기만 하면 된다.

 

#03. 최적화

 

[최적화]

웹 서비스의 성능을 개선, 사용자의 불필요한 대기 시간을 줄이고 사용자 경험을 긍정적으로 개선

- useMemo: 메모리제이션 기법을 이용해서 연산의 결값을 저장. 동일 요청이 들어오면 결괏값을 빠르게 응답

 

[고차 컴포넌트와 횡단 관심사]

React.memo

 

-HOC: Higher Order Component, 인수로 전달된 컴포넌트를 새로운 컴포넌트로 반환하는 함수

어떤 기능을 추가해서 반환

강화된 컴포넌트라고 함

 

-횡단 관심사: 크로스 커팅 관심사 Cross-Cutting Concerns

비즈니스 로직과 공통된 기능을 구분

중복코드를 줄일 수 있음

 

- 다음과 같이 React.memo하면 지속적인 리랜더링이 일어나지 않음

import './Header.css';
import React from 'react';

export default React.memo(() => {
    console.log('header');
    return (
        <div className="Header">
            <h3>오늘은 📅</h3>
            <h1>{new Date().toDateString()}</h1>
        </div>
    );
});

- React.memo가 값이 변화한지 안한지 판단하는 요소: Property

 

[useCallback]

불필요한 상황에서 리랜더 방지

useCallback(함수, []);

 

[최적화 규칙]

- 최적화는 꼭 필요할 때만 하기 : 오히려 더 복잡해져서 문제 일으킬 수도

- 최적화는 가장 마지막에

- 컴포넌트 구조 잘 설계했는지 확인하기