React가 아닌 위젯 제어하기
// 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 구문을 사용하여 다시 작성할 수도 있지만, 그렇더라고 여전히 클린업 함수는 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 앱에서는 특히 그렇다.
이펙트는 서버에서 실행되지 않는다.