React.useEffect 경쟁 조건을 조심하세요 🐛 버그
14836 단어 reactjavascript
useEffect
에서 Race Condition Bug를 소개하는 것은 매우 일반적입니다. 이것은 React.useEffect
내부에 비동기 코드가 있을 때마다 발생할 수 있습니다.레이스 컨디션 버그란 무엇입니까?
경쟁 조건은 동일한 값을 업데이트하는 두 개의 비동기 프로세스가 있을 때 발생할 수 있습니다. 이 시나리오에서는 값 업데이트를 완료하는 마지막 프로세스입니다.
이것은 우리가 원하는 것이 아닐 수도 있습니다. 값을 업데이트하기 위해 마지막 프로세스가 시작되기를 원할 수 있습니다.
예를 들어 데이터를 가져온 다음 데이터를 다시 렌더링하고 다시 가져오는 구성 요소가 있습니다.
경쟁 조건 구성 요소의 예
이것은 Race Condition Bug가 있을 수 있는 구성 요소의 예입니다.
import { useEffect, useState } from "react";
import { getPerson } from "./api";
export const Race = ({ id }) => {
const [person, setPerson] = useState(null);
useEffect(() => {
setPerson(null);
getPerson(id).then((person) => {
setPerson(person);
};
}, [id]);
return person ? `${id} = ${person.name}` : null;
}
언뜻 보기에 이 코드에는 아무런 문제가 없어 보이지만 이것이 이 버그를 매우 위험하게 만들 수 있습니다.
useEffect
은 id
이 변경될 때마다 실행되고 getPerson
을 호출합니다. getPerson
이 시작되고 id
이 변경되면 getPerson
에 대한 두 번째 호출이 시작됩니다.첫 번째 호출이 두 번째 호출보다 먼저 완료되면 첫 번째 호출의 데이터로
person
을 덮어쓰므로 애플리케이션에 버그가 발생합니다.중단 컨트롤러
fetch
을 사용할 때 AbortController
을 사용하여 첫 번째 요청을 수동으로 중단할 수 있습니다.참고: 나중에 이 작업을 수행하는 더 간단한 방법을 찾을 것입니다. 이 코드는 교육용입니다.
import { useEffect, useRef, useState } from "react";
import { getPerson } from "./api";
export const Race = ({ id }) => {
const [data, setData] = useState(null);
const abortRef = useRef(null);
useEffect(() => {
setData(null);
if (abortRef.current != null) {
abortRef.current.abort();
}
abortRef.current = new AbortController();
fetch(`/api/${id}`, { signal: abortRef.current.signal })
.then((response) => {
abortRef.current = null;
return response;
})
.then((response) => response.json())
.then(setData);
}, [id]);
return data;
}
이전 요청 취소
일부 비동기 코드가
AbortController
에서 작동하지 않기 때문에 AbortController
은 항상 우리를 위한 옵션은 아닙니다. 따라서 이전 비동기 호출을 취소할 방법이 여전히 필요합니다.이것은
cancelled
내부에 useEffect
플래그를 설정하여 가능합니다. true
의 id
기능을 사용하여 unmount
이 변경될 때 이를 useEffect
으로 설정할 수 있습니다.참고: 나중에 이 작업을 수행하는 더 간단한 방법을 찾을 것입니다. 이 코드는 교육용입니다.
import { useEffect, useState } from "react";
import { getPerson } from "./api";
export const Race = ({ id }) => {
const [person, setPerson] = useState(null);
useEffect(() => {
let cancelled = false;
setPerson(null);
getPerson(id).then((person) => {
if (cancelled) return; // only proceed if NOT cancelled
setPerson(person);
};
return () => {
cancelled = true; // cancel if `id` changes
};
}, [id]);
return person ? `${id} = ${person.name}` : null;
}
반응 쿼리 사용
각 구성 요소 내에서 수동으로 중단 또는 취소를 처리하지 않는 것이 좋습니다. 대신 해당 기능을 React Hook 내부에 래핑해야 합니다. 다행히도 이미 우리를 위해 그렇게 해 준 라이브러리가 있습니다.
react-query 라이브러리를 사용하는 것이 좋습니다. 이 라이브러리는 경합 상태 버그를 방지할 뿐만 아니라 캐싱, 재시도 등과 같은 다른 유용한 기능을 제공합니다.
나는 또한 react-query이 코드를 단순화하는 방식을 좋아합니다.
import { useQuery } from "react-query";
import { getPerson } from "./api";
export const Race = ({ id }) => {
const { isLoading, error, data } = useQuery(
["person", id],
(key, id) => getPerson(id)
);
if (isLoading) return "Loading...";
if (error) return `ERROR: ${error.toString()}`;
return `${id} = ${data.name}`;
}
react-query에 대한 첫 번째 인수는 캐시 키이고 두 번째 인수는 캐시가 없거나 캐시가 부실하거나 유효하지 않을 때 호출되는 함수입니다.
요약
경쟁 조건 버그는 React.useEffect
내부에 비동기 호출이 있고 React.useEffect
이 다시 실행될 때 발생할 수 있습니다.
fetch
을 사용할 때 요청을 중단할 수 있습니다. Promise
취소 가능합니다. 그러나 각 구성 요소에 대해 해당 코드를 수동으로 작성하지 않고 대신 react-query과 같은 라이브러리를 사용하는 것이 좋습니다.
joel.net에서 내 뉴스 레터를 구독하십시오.
트위터나 유튜브에서 나를 찾아줘
건배 🍻
Reference
이 문제에 관하여(React.useEffect 경쟁 조건을 조심하세요 🐛 버그), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://dev.to/joelnet/beware-of-react-useeffect-race-condition-bugs-42hl
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
Reference
이 문제에 관하여(React.useEffect 경쟁 조건을 조심하세요 🐛 버그), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/joelnet/beware-of-react-useeffect-race-condition-bugs-42hl텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)