[React] useEffect

2022. 2. 8. 18:27·React/기능
목차
  1. useEffect???
  2. 파라미터를 좀 더 깊게 알아보기
  3. 파라미터에 특정 값 넣기
  4. 파라미터 생략하기
  5. 파라미터 의존성
  6. state, props
반응형

useEffect???

componentDidMount와 componentDidUpdate, componentWillUnmount 클래스 컴포넌트에서 제공하는

Lifecycle API를 useEffect를 사용하여 대신할 수 있다!!

 

render가 발생할 때마다 (componentDidMount ⇒ 초기, componentDidUpdate ⇒ 매번) effect가 실행된다.

 

여기서 두 번째 파라미터인 inputs를 통해 특정 상태가 update 되었을 때만 effect가 실행되게 설정할 수 있다.

만약 useEffect의 inputs에 빈 배열을 넘겨주면 최초(componentsDidMount)에만 실행된다.

componentWillUnmount를 실행하기 위해선 return을 사용하여 기능 수행이 가능하다.

useEffect(() => {
  window.addEventListener("mousemove", logMousePosition);
  return () => {
    window.removeEventListener("mousemove", logMousePosition);
  };
}, []);

effect 함수에 return 값이 있는 경우 hook의 cleanup 함수로 인식하고

다음 effect가 실행되기 전 실행해준다. 여기서 componentWillUnmount는 컴포넌트가 사라지기 전

한 번만 실행했다면, cleanup 함수는 새로운 effect 실행 전에 매번 호출된다.

만약 빈 배열을 두 번째 파라미터로 지정한다면, unmount 될 때 한번만 실행된다.

주로, 마운트 시 하는 작업들은 다음과 같다.

  • props로 받은 값을 컴포넌트의 로컬 상태로 설정
  • 외부 API 요청(REST API 등등)
  • 라이브러리 사용
  • setInterval을 통한 반복 작업 or setTimeout을 통한 작업 예약

그리고 언마운트 시 하는 작업들은 다음과 같다.

  • setInterval, setTimeout을 사용하여 등록한 작업들 clear 하기
  • 라이브러리 인스턴스 제거

파라미터를 좀 더 깊게 알아보기

두 번째 파라미터인 배열에 따라 어떻게 수행되는지 좀 더 깊게 알아보자

파라미터에 특정 값 넣기

파라미터에 특정 값을 넣어보면, 컴포넌트가 처음 마운트 될 때마다 호출되고, 지정한 값이 바뀔 때

호출된다. 그리고 언마운트 시에도 호출되고, 값이 바뀌기 직전에도 호출된다.

import React, { useEffect } from 'react';

function User({ user, onRemove, onToggle }) {
  useEffect(() => {
    console.log('user 값이 설정됨');
    console.log(user);
    return () => {
      console.log('user 가 바뀌기 전..');
      console.log(user);
    };
  }, [user]);
  return (
    <div>
      <b
        style={{
          cursor: 'pointer',
          color: user.active ? 'green' : 'black'
        }}
        onClick={() => onToggle(user.id)}
      >
        {user.username}
      </b>
      &nbsp;
      <span>({user.email})</span>
      <button onClick={() => onRemove(user.id)}>삭제</button>
    </div>
  );
}

function UserList({ users, onRemove, onToggle }) {
  return (
    <div>
      {users.map(user => (
        <User
          user={user}
          key={user.id}
          onRemove={onRemove}
          onToggle={onToggle}
        />
      ))}
    </div>
  );
}

export default UserList;

useEffect 안에서 사용하는 state나 props가 있다면, useEffect의 파라미터에 넣어주는 게 규칙이다.

만약 useEffect 안에 사용하는 state나 props를 파라미터 안에 넣지 않게 된다면 useEffect에 등록한

함수가 실행될 때 최신 state나 props를 가리키지 않게 된다.

파라미터 생략하기

파라미터를 생략하면, 컴포넌트가 리 렌더링 될 때마다 호출된다!

import React, { useEffect } from 'react';

function User({ user, onRemove, onToggle }) {
  useEffect(() => {
    console.log(user);
  });
  return (
    <div>
      <b
        style={{
          cursor: 'pointer',
          color: user.active ? 'green' : 'black'
        }}
        onClick={() => onToggle(user.id)}
      >
        {user.username}
      </b>
      &nbsp;
      <span>({user.email})</span>
      <button onClick={() => onRemove(user.id)}>삭제</button>
    </div>
  );
}

function UserList({ users, onRemove, onToggle }) {
  return (
    <div>
      {users.map(user => (
        <User
          user={user}
          key={user.id}
          onRemove={onRemove}
          onToggle={onToggle}
        />
      ))}
    </div>
  );
}

export default UserList;

※ 참고로고로 리액트 컴포넌트는 기본적으로 부모 컴포넌트가 리 렌더링 되면 자식 컴포넌트 또한

리렌더링 된다. 바뀐 내용이 없다고 하더라도...

파라미터 의존성

state, props

위 useEffect에서 state와 props를 사용한다면 파라미터에 적어주는 것이 규칙이라고 했다.

하지만

function Counter() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    const id = setInterval(() => {
      setCount(count + 1);
    }, 1000);
    return () => clearInterval(id);
  }, [count]);
  return <h1>{count}</h1>;
}

위 코드에선 count를 초당 1씩 증가시키게 하기 위해서 count를 파라미터에 넣는다.

하지만 여기서의 count의 역할은 이전 count 값을 나타내는 것이다.

 

[count]를 통해 count를 사용할 때의 규칙을 지켰다. 근데레레레 그로 인해 setCount로 count값이

변경되면 또다시 useEffect가 실행되어버린다?!!

 

그래서 값이 바뀌면 return을 통해 기존 Interval을 없애준다.

 

하지만 이 말은 즉, count 값이 바뀔 때마다 인터벌은 해체되고 다시 설정된다.

여기서 파라미터를 없앴지만 구현하는 방법은 있을까??

function Counter() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    const id = setInterval(() => {
      setCount(c => c + 1);
    }, 1000);
    return () => clearInterval(id);
  }, []);
  return <h1>{count}</h1>;
}

Wow! 해결해따! 위와 같이 setCount처럼 useState로 만든 상태 변화 함수는 이전 값을 파라미터로

받아올 수 있다.

 

하지만 만약 1씩 증가는 것이 아닌 step이라는 상태를 통해 원하는 값만큼 증가시키면 어떨까??

function Counter() {
  const [count, setCount] = useState(0);
  const [step, setStep] = useState(1);
  useEffect(() => {
    const id = setInterval(() => {
      setCount(c => c + step);
    }, 1000);
    return () => clearInterval(id);
  }, [step]);
  return (
    <>
      <h1>{count}</h1>
      <input value={step} onChange={e => setStep(Number(e.target.value))} />
    </>
  );
}

위 count를 파라미터로 주었을 때와 같은 상황이 발생해버린다.

step이 변하면 다시 인터벌을 시작하게 된다. 의존성으로 인해...

 

물론 이것을 원한다면 상관없지만 step이 바뀐다고 인터벌 시계가 초기화되지 않는 것을 원한다면?

즉, 파라미터에서 step을 제거하고 싶다면 어떻게 해야 할까??

 

이때, 두 state 모두 useReducer로 교체해야 한다.

 

리듀서는 컴포넌트에서 일어나는 "액션"의 표현과 그 반응으로 state가 어떻게 업데이트되어야 할지

분리한다.

const [state, dispatch] = useReducer(reducer, initialState);
const { count, step } = state;
useEffect(() => {
  const id = setInterval(() => {
    dispatch({ type: 'tick' }); // setCount(c => c + step) 대신에
  }, 1000);
  return () => clearInterval(id);
}, [dispatch]);

리액트는 컴포넌트가 유지되는 한 dispatch 함수가 항상 같다는 것을 보장해준다.

const initialState = {
  count: 0,
  step: 1,
};
function reducer(state, action) {
  const { count, step } = state;
  if (action.type === 'tick') {
    return { count: count + step, step };
  } else if (action.type === 'step') {
    return { count, step: action.step };
  } else {
    throw new Error();
  }
}

리덕스는 위와 같이 스토어와 액션을 선언하고 사용하는 컴포넌트에서 액션을 디스 패치하여

useEffect에서 step 상태로부터 분리시켰다.

 

위와 같은 과정으로 useEffect가 이전 상태를 기준으로 state를 설정할 필요가 있을 때 의존성을

어떻게 제거해야 하는지 공부했다.

 

But 다음 상태를 계산하는데, props가 필요하다면??

step이 <Count step={1} /> 이렇게 props로 주어 props.step이라면??

의존성 파라미터에 props.step을 설정하는 것을 피할 수 없을 것이다.....

 

※사실 피하려면 피할 순 있다...!

 function Counter({ step }) {
  const [count, dispatch] = useReducer(reducer, 0);
  function reducer(state, action) {
    if (action.type === 'tick') {
      return state + step;
    } else {
      throw new Error();
    }
  }
  useEffect(() => {
    const id = setInterval(() => {
      dispatch({ type: 'tick' });
    }, 1000);
    return () => clearInterval(id);
  }, [dispatch]);
  return <h1>{count}</h1>;
}

리듀서를 컴포넌트 안에 정의하여 props를 읽도록 하면 된다.

 

하지만 이 방식은 최적화를 무효화할 수 있기 때문에 난발해선 안된다.

반드시 필요하면 이렇게 구현은 가능하다!

반응형
저작자표시 비영리 변경금지

'React > 기능' 카테고리의 다른 글

[React] useLayoutEffect  (0) 2022.02.08
[React] useRef  (0) 2022.02.08
[React] useState  (0) 2022.02.08
[React] Route 부가적인 부분 (history, withRouter..)  (0) 2022.02.08
[React] Route  (0) 2022.02.08
  1. useEffect???
  2. 파라미터를 좀 더 깊게 알아보기
  3. 파라미터에 특정 값 넣기
  4. 파라미터 생략하기
  5. 파라미터 의존성
  6. state, props
'React/기능' 카테고리의 다른 글
  • [React] useLayoutEffect
  • [React] useRef
  • [React] useState
  • [React] Route 부가적인 부분 (history, withRouter..)
잉여개발자
잉여개발자
풀스택 개발자를 목표로 잉여롭게 개발 공부도 하면서 다양한 취미 생활도 즐기고 있는 잉여 개발자입니다.
  • 잉여개발자
    잉여로운 개발일지
    잉여개발자
    • 분류 전체보기 (789)
      • 개발정보 (36)
      • 개발환경 (7)
      • 개발생활 (19)
      • React (141)
        • 이론 (23)
        • 기능 (12)
        • 실험실 (88)
        • 버그 (6)
        • 패스트캠퍼스 (9)
        • Npm (3)
      • React Native (28)
        • 공통 (6)
        • TypeScript (3)
        • JavaScript (18)
        • 버그 (1)
      • Next.js (30)
        • 이론 (13)
        • 실험실 (13)
        • 버그 (3)
      • Web (35)
      • 알고리즘 (202)
        • 풀이 힌트 (39)
      • JavaScript (47)
      • TypeScript (29)
        • 기초 (27)
        • 실험실 (2)
      • Node.js (13)
        • 이론 (0)
        • 기능 (3)
        • 실험실 (9)
        • 버그 (1)
      • 도커 (4)
      • CCNA (22)
        • 이론 (4)
        • 문제 (18)
      • 취미생활 (167)
        • 잉여로운 칵테일 (2)
        • 잉여의 식물키우기 (130)
        • 잉여로운 여행기 (11)
        • 잉여의 제2외국어 (21)
        • 잉여로운 책장 (2)
      • Java (1)
        • Java의 정석 (1)
      • 꿀팁 공유 (3)
  • 태그

    영어독학
    리액트
    react
    webpack
    javascript
    바질
    식물
    next.js
    Babel
    Node.js
    타입스크립트
    typescript
    덤프
    CSS
    바질 키우기
    ChatGPT
    redux
    CCNA
    다이소
    리얼클래스
    프로그래머스
    Docker
    리얼학습일기
    네트워크
    타일러영어
    영어회화
    자바스크립트
    네이버 부스트캠프
    ReactNative
    알고리즘
  • hELLO· Designed By정상우.v4.10.1
잉여개발자
[React] useEffect

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.