[React #12] The Effect Hook (feat.useEffect())

Kayoung Kim·2022년 1월 5일
0

React

목록 보기
12/15
post-thumbnail

웹 사이트를 만들다 보면 화면에 보일 수 있는 데이터를 서버에서 받아오기도 해야 하고, state가 바뀔 때마다 함수를 실행시키거나, 이벤트 리스너를 달았다가 해제하는 등의 동작이 필요할 수 있다. useEffect hook은 바로 이럴 때 유용하게 쓸 수 있다.

  • Hooks가 나오기 이전에는 함수 컴포넌트는 props 폼의 데이터를 받아오거나 렌더되는 JSX를 리턴하는 역할에 그치지 않았다. 그러나, State Hook은 함수 컴포넌트의 컴포넌트 state 폼 안에서 dynamic data를 처리할 수 있게 해주었다.
  • Effect Hook은 다음과 같이 자바스크립트 코드가 실행된 후 작동한다.
    - 백엔드 서버에서 데이터를 fetching 할 때
    • 데이터의 스트림을 subscribing 할 때
    • 타이머, 인터벌을 매니징할 때
    • DOM에서 변화하는 요소를 읽어낼 때
  • 위와 같은 요소에서 각각의 렌더링 후 일어나는 이유는 'side effects(부수 효과)' 때문이다.
  • Effect Hook이 사용되는 세가지 key moments
  1. 컴포넌트가 DOM에 처음으로 추가/마운트 되서 렌더링 될 때
  2. state/props가 바뀌어서 컴포넌트가 재렌더링될 때
  3. 컴포넌트가 DOM에서 지워지거나/언마운트될 때
//Class.js
import React, {Component} from 'react';

export default class PageTitle extends Component {
  constructor(props) {
    super(props);
    this.state = {
      name: ''
    };
  }

  componentDidMount() {
    document.title = this.state.name;
  }
  
  componentDidUpdate() {
    document.title == `Hi, ${this.state.name}`;
  }

  render() {
    return (
      <div>
        <p>Use the input field below to rename this page!</p>
        <input 
          onChange={({target}) => this.setState({ name: target.value })} 
          value={this.state.name} 
          type='text' />
      </div>
    );
  }
}

//Function.js
import React, {Component} from 'react';

export default class PageTitle extends Component {
  constructor(props) {
    super(props);
    this.state = {
      name: ''
    };
  }

  componentDidMount() {
    document.title = this.state.name;
  }
  
  componentDidUpdate() {
    document.title == `Hi, ${this.state.name}`;
  }

  render() {
    return (
      <div>
        <p>Use the input field below to rename this page!</p>
        <input 
          onChange={({target}) => this.setState({ name: target.value })} 
          value={this.state.name} 
          type='text' />
      </div>
    );
  }
}
  • class형에서 componentDidMount()``componentDidUpdate()로 구분해서 세팅된 것이 함수형에서는useEffect()로 한번에 처리할 수 있어 효율적이다.

The Effect Hook - useEffect()

  • useEffect를 react 라이브러리에서 import 한다.
    import React, {useEffect} from 'react';
  • Effect Hook은 우리가 안에 있는 다른 함수를 호출하기 위해 사용하기 때문에 useEffect() 함수를 호출할 때는 아무것도 리턴하지 않는다.
  • useEffect() 함수에 첫번째 인자는 우리가 리액트 컴포넌트가 리턴될 때 부르고 싶은 콜백 함수다.
import React, { useState, useEffect } from 'react';
 
function PageTitle() {
  const [name, setName] = useState('');
 
  useEffect(() => {
    document.title = `Hi, ${name}`;
  });
 
  return (
    <div>
      <p>Use the input field below to rename this page!</p>
      <input onChange={({target}) => setName(target.value)} value={name} type='text' />
    </div>
  );
}
  • 위의 예시에서 effect는 () => { document.title = name; }가 된다.

effect안에서 현재 state는 어떻게 사용될까?

  • effect가 컴포넌트가 렌더링 된 후 에 일어난다고 하더라도 함수 컴포넌트 스코프에 접근이 가능하다.
  • 리액트가 컴포넌트를 렌더링하면 DOM이 업데이트 되고, 그 후 effect가 일어난다. => 처음부터 끝까지 모든 렌더링에서 발생한다.

Clean up Effects

  • 이벤트 리스너를 여러개 붙일 때는 Effect를 제거해주는 것이 필요하다.
    이벤트를 제거해줌으로써 불필요한 메모리가 낭비되는 것을 막을 수 있다.
  • clean up fuction을 설정해주지 않으면 새로운 이벤트 리스너가 컴포넌트가 재렌더링 될 때마다 DOM의 document object에 붙는다.
    => 버그를 유발하거나 퍼포먼스가 감소할 수 있다.
  • effect는 모든 렌더링 후에 발생하기 때문에 cleanup 함수 또한 각각의 재렌더링 때마다, 언마운팅 전에 일어난다.
  • 모든 effect가 함수를 리턴하기 때문에 useEffect() Hook은 항상 cleanup function으로 인식한다. cleanup function은 선택사항이지만, 메모리 손실이 큰 코드의 경우 써주는 것이 좋다.
useEffect(()=>{
  document.addEventListener('keydown', handleKeyPress);
  return () => {
    document.removeEventListener('keydown', handleKeyPress);
  };
})

Control When Effects are Called - dependency array([])

  • useEffect()에서 첫번째 인자 함수가 호출될 때 컴포넌트가 렌더링 될 때마다 실행된다.
  • effect가 첫번째 렌더링 이후에만 호출되기를 원한다면 두번째 인자에 빈 배열을 넣어준다. 이를 dependency array(의존성 배열)이라고 부른다.
  • 빈 의존성 배열은 1) 컴포넌트가 처음 마운트될 때 effect를 불러오고, 2) 컴포넌트가 언마운트 될 떄 cleanup function이 effect에 의해 리턴될 때 호출한다.
useEffect(() => {
  alert("component rendered for the first time");
  return () => {
    alert("component is being removed from the DOM");
  };
}, []); 
  • 위의 예시에서 빈 배열이 없다면, alerts은 컴포넌트가 렌더링 될 때마다 실행된다.

Fetch Data

  • 컴포넌트에서 데이터를 받을 때도 빈 배열을 넣어주면, 첫 렌더링 이후 데이터를 fetch 할 수 있다.
  • 서버로부터 응답을 불러올 때, state Hook에서 state setter를 설정해 로컬 컴포넌트에 있는 서버의 response의 데이터를 저장할 수 있다.
    => State Hook과 Effect Hook을 함께 사용하면 매 렌더링마다 불필요한 새로운 데이터를 fetching해 오는 것을 막을 수 있다.
useEffect(() => {
  document.title = `You clicked ${count} times`;
}, [count]); 
// Only re-run the effect if the value stored by count changes
  • 빈 배열의 의존성 배열은 의존할 요소가 없기 때문에 다시 실행될 필요가 없다. 즉, effect가 바뀌지 않고 한번만 호출되면 된다는 것이다.
  • 빈 배열이 아닌 의존성 배열은 의존성 배열의 변수 값이 변화하면 재실행되고, 그렇지 않으면 실행되지 않는다. 즉 변화가 일어나면 effect는 다시 호출된다.

Rules of Hooks

Hooks을 쓸 때는 두 가지의 규칙이 있다.
1) top level에 쓴다.
- React에서 Virtual DOM을 빌드할 떄, 라이브러리는 유저 인터렉션에 따라 우리가 정의한 컴포넌트를 계속해서 정의한다. React는 정의된 컴포넌트의 함수에서 Hook으로 불러온 데이터를 계속 추적한다. 이러한 이유로, Hook을 가장 위에서 호출해야 한다.
- loops, conditions, nested functions 안에 쓸 수 없다.

if (userName !== '') {
  useEffect(() => {
    localStorage.setItem('savedUserName', userName);
  });
}

useEffect(() => {
  if (userName !== '') {
    localStorage.setItem('savedUserName', userName);
  }
});

2) React 함수형으로 쓴다.

  • Hooks는 클래스형에서 쓸 수 없고, 함수형 컴포넌트에서 쓸 수 있다.
  • 다른 곳에서 쓸 수 있는 유일한 경우는 custom hooks로 쓸 때다.

0개의 댓글