[2024.06.04] TIL 33일차

김선민·2024년 6월 4일

# useEffect

React가 아닌 위젯 제어하기

  • 외부 시스템을 컴포넌트의 특정 props나 state와 동기화하고 싶을 때가 있다.
  • 예를 들어 React 없이 작성된 타사 맵 위젯이나 비디오 플레이 컴포넌트가 있는 경우,
    Effect를 사용하여 state를 React 컴포넌트의 현재 state와 일치시키는 메서드를 호출할 수 있다.
	// App.js
    import { useState } from 'react';
    import Map from './Map.js';

    export default function App() {
      const [zoomLevel, setZoomLevel] = useState(0);
      return (
        <>
          Zoom level: {zoomLevel}x
          <button onClick={() => setZoomLevel(zoomLevel + 1)}>+</button>
          <button onClick={() => setZoomLevel(zoomLevel - 1)}>-</button>
          <hr />
          <Map zoomLevel={zoomLevel} />
        </>
      );
    }
	// Map.js
    
    import { useRef, useEffect } from 'react';
    import { MapWidget } from './map-widget.js';

    export default function Map({ zoomLevel }) {
      const containerRef = useRef(null);
      const mapRef = useRef(null);

      useEffect(() => {
        if (mapRef.current === null) {
          mapRef.current = new MapWidget(containerRef.current);
        }

        const map = mapRef.current;
        map.setZoom(zoomLevel);
      }, [zoomLevel]);

      return (
        <div
          style={{ width: 200, height: 200 }}
          ref={containerRef}
        />
      );
    }
	// map-widget.js
    
    import 'leaflet/dist/leaflet.css';
    import * as L from 'leaflet';

    export class MapWidget {
      constructor(domNode) {
        this.map = L.map(domNode, {
          zoomControl: false,
          doubleClickZoom: false,
          boxZoom: false,
          keyboard: false,
          scrollWheelZoom: false,
          zoomAnimation: false,
          touchZoom: false,
          zoomSnap: 0.1
        });
        L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
          maxZoom: 19,
          attribution: '© OpenStreetMap'
        }).addTo(this.map);
        this.map.setView([0, 0], 0);
      }
      setZoom(level) {
        this.map.setZoom(level);
      }
    }
  • 이 예제에서는 MapWidget 클래스가 자신에게 전달된 DOM 노드만 관리하기 때문에 클린업 함수가 필요하지 않다.
  • Map React 컴포넌트가 트리에서 제거된 후, DOM 노드와 MapWidget 클래스 인스턴스는 브라우저 JavaScript 엔진에 의해 자동으로 가비지컬렉팅 된다.

Effect로 데이터 페칭하기

  • Effect를 사용하여 컴포넌트에 대한 데이터를 패치할 수 있다.

  • 프레임워크를 사용하는 경우, 프레임워크의 데이터 패칭 메커니즘을 사용하는 것이 Effect를 수동으로 작성하는 것 보다 훨씬 효율적이라는 점에 유의해야 한다.

  • Effect에서 데이터를 수동으로 패치하려는 경우 코드는 다음과 같을 수 있다.

  • ignore 변수는 false로 초기화되고 클린업 중에 true로 설정된다.

  • 이렇게히면 네트워크 응답이 보낸 순서와 다른 순서로 도착하더라고 "조건 경합"이 발생하지 않는다.

    import { useState, useEffect } from 'react';
    import { fetchBio } from './api.js';

    export default function Page() {
      const [person, setPerson] = useState('Alice');
      const [bio, setBio] = useState(null);

      useEffect(() => {
        let ignore = false;
        setBio(null);
        fetchBio(person).then(result => {
          if (!ignore) {
            setBio(result);
          }
        });
        return () => {
          ignore = true;
        };
      }, [person]);

      // ...

  • async / await 구문을 사용하여 다시 작성할 수도 있지만, 그렇더라고 여전히 클린업 함수는
    제공해야 한다.
  • Effect에서 직접 데이터를 패칭하는 작업을 반복적으로 작성하면 나중에 캐싱 및 서버 렌더린과 같은 최적화를 추가하기가 어려워진다.
  • 직접 만들거나 커뮤니티에서 유지 관리하는 커스텀 훅을 사용하는 것이 더 쉽다.
    import { useState, useEffect } from 'react';
    import { fetchBio } from './api.js';

    export default function Page() {
      const [person, setPerson] = useState('Alice');
      const [bio, setBio] = useState(null);
      useEffect(() => {
        let ignore = false;
        setBio(null);
        fetchBio(person).then(result => {
          if (!ignore) {
            setBio(result);
          }
        });
        return () => {
          ignore = true;
        }
      }, [person]);

      return (
        <>
          <select value={person} onChange={e => {
            setPerson(e.target.value);
          }}>
            <option value="Alice">Alice</option>
            <option value="Bob">Bob</option>
            <option value="Taylor">Taylor</option>
          </select>
          <hr />
          <p><i>{bio ?? 'Loading...'}</i></p>
        </>
      );
    }

Effect에서 데이터 패칭하는 것을 대체할 좋은 대안

  • Effect 내부에 fetch를 작성하는 것은 데이터를 패치하는 인기있는 방법으로, 완전한 client-side 앱에서는 특히 그렇다.

    • 하지만 이는 매우 수동적인 접근 방식이며 상당한 단점이 있다.
  • 이펙트는 서버에서 실행되지 않는다.

    • 즉, 서버에서 렌더링 되는 초기 HTML에는 데이터가 없는 로딩 state만 포함된다.
    • 클라이언트 컴퓨터는 모든 JavaScript를 다운로드하고 앱을 렌더링 해야만 데이터를
      로드한다는 것을 알 수 있다.
    • 이는 매우 비효율적이다.












profile
웹 프론트엔드

0개의 댓글