[javascript] useEffect의 비동기 함수에 대한 React Hook 경고 : useEffect 함수는 정리 함수를 반환하거나 아무것도 반환하지 않아야합니다.
아래와 같은 useEffect 예제를 시도했습니다.
useEffect(async () => {
try {
const response = await fetch(`https://www.reddit.com/r/${subreddit}.json`);
const json = await response.json();
setPosts(json.data.children.map(it => it.data));
} catch (e) {
console.error(e);
}
}, []);
내 콘솔에이 경고가 표시됩니다. 그러나 정리는 내가 생각하는 비동기 호출의 경우 선택 사항입니다. 이 경고를받는 이유를 잘 모르겠습니다. 예제를 위해 샌드 박스 연결. https://codesandbox.io/s/24rj871r0p
답변
나는 Dan Abramov (React 핵심 관리자 중 한 명) 대답을 여기 에서 볼 것을 제안합니다 .
필요 이상으로 복잡하게 만들고 있다고 생각합니다.
function Example() {
const [data, dataSet] = useState<any>(null)
useEffect(() => {
async function fetchMyAPI() {
let response = await fetch('api/data')
response = await response.json()
dataSet(response)
}
fetchMyAPI()
}, [])
return <div>{JSON.stringify(data)}</div>
}
장기적으로는이 패턴이 경쟁 조건을 조장하기 때문에 권장하지 않을 것입니다. 예를 들어, 호출 시작과 종료 사이에 어떤 일이 발생할 수 있으며 새로운 소품을 얻을 수 있습니다. 대신 데이터 가져 오기를 위해 Suspense를 권장합니다.
const response = MyAPIResource.read();
효과가 없습니다. 그러나 그 동안 비동기 항목을 별도의 함수로 이동하여 호출 할 수 있습니다.
여기에서 실험적 서스펜스 에 대해 자세히 알아볼 수 있습니다 .
eslint와 함께 외부 기능을 사용하려는 경우.
function OutsideUsageExample() {
const [data, dataSet] = useState<any>(null)
const fetchMyAPI = useCallback(async () => {
let response = await fetch('api/data')
response = await response.json()
dataSet(response)
}, [])
useEffect(() => {
fetchMyAPI()
}, [fetchMyAPI])
return (
<div>
<div>data: {JSON.stringify(data)}</div>
<div>
<button onClick={fetchMyAPI}>manual fetch</button>
</div>
</div>
)
}
useCallback 사용 useCallback . 샌드 박스 .
import React, { useState, useEffect, useCallback } from "react";
export default function App() {
const [counter, setCounter] = useState(1);
// if counter is changed, than fn will be updated with new counter value
const fn = useCallback(() => {
setCounter(counter + 1);
}, [counter]);
// if counter is changed, than fn will not be updated and counter will be always 1 inside fn
/*const fnBad = useCallback(() => {
setCounter(counter + 1);
}, []);*/
// if fn or counter is changed, than useEffect will rerun
useEffect(() => {
if (!(counter % 2)) return; // this will stop the loop if counter is not even
fn();
}, [fn, counter]);
// this will be infinite loop because fn is always changing with new counter value
/*useEffect(() => {
fn();
}, [fn]);*/
return (
<div>
<div>Counter is {counter}</div>
<button onClick={fn}>add +1 count</button>
</div>
);
}
답변
다음과 같은 비동기 함수를 사용할 때
async () => {
try {
const response = await fetch(`https://www.reddit.com/r/${subreddit}.json`);
const json = await response.json();
setPosts(json.data.children.map(it => it.data));
} catch (e) {
console.error(e);
}
}
약속을 반환하고 useEffect
콜백 함수가 약속을 반환하는 기대하지 않는, 오히려 아무것도 반환되지 않습니다 또는 함수가 반환되는 것을 기대하고있다.
경고에 대한 해결 방법으로 자체 호출 비동기 함수를 사용할 수 있습니다.
useEffect(() => {
(async function() {
try {
const response = await fetch(
`https://www.reddit.com/r/${subreddit}.json`
);
const json = await response.json();
setPosts(json.data.children.map(it => it.data));
} catch (e) {
console.error(e);
}
})();
}, []);
또는 더 깔끔하게 만들기 위해 함수를 정의한 다음 호출 할 수 있습니다.
useEffect(() => {
async function fetchData() {
try {
const response = await fetch(
`https://www.reddit.com/r/${subreddit}.json`
);
const json = await response.json();
setPosts(json.data.children.map(it => it.data));
} catch (e) {
console.error(e);
}
};
fetchData();
}, []);
두 번째 솔루션을 사용하면 읽기가 더 쉬워지고 새 요청이 실행되면 이전 요청을 취소하거나 최신 요청 응답을 상태로 저장하는 코드를 작성하는 데 도움이됩니다.
답변
React가 더 나은 방법을 제공 할 때까지 도우미를 만들 수 있습니다 useEffectAsync.js
.
import { useEffect } from 'react';
export default function useEffectAsync(effect, inputs) {
useEffect(() => {
effect();
}, inputs);
}
이제 비동기 함수를 전달할 수 있습니다.
useEffectAsync(async () => {
const items = await fetchSomeItems();
console.log(items);
}, []);
답변
이 질문을 읽었고 useEffect를 구현하는 가장 좋은 방법이 답변에 언급되어 있지 않다고 생각합니다. 네트워크 통화가 있고 응답을 받으면 무언가를하고 싶다고 가정 해 보겠습니다. 단순화를 위해 네트워크 응답을 상태 변수에 저장해 보겠습니다. 조치 / 감소기를 사용하여 네트워크 응답으로 상점을 업데이트 할 수 있습니다.
const [data, setData] = useState(null);
/* This would be called on initial page load */
useEffect(()=>{
fetch(`https://www.reddit.com/r/${subreddit}.json`)
.then(data => {
setData(data);
})
.catch(err => {
/* perform error handling if desired */
});
}, [])
/* This would be called when store/state data is updated */
useEffect(()=>{
if (data) {
setPosts(data.children.map(it => {
/* do what you want */
}));
}
}, [data]);
참조 => https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects
답변
시험
const MyFunctionnalComponent: React.FC = props => {
useEffect(() => {
// Using an IIFE
(async function anyNameFunction() {
await loadContent();
})();
}, []);
return <div></div>;
};
답변
다른 독자의 경우 비동기 함수를 래핑하는 대괄호가 없다는 사실에서 오류가 발생할 수 있습니다.
비동기 함수 initData 고려
async function initData() {
}
이 코드는 오류로 이어집니다.
useEffect(() => initData(), []);
그러나 이것은 다음을 수행하지 않습니다.
useEffect(() => { initData(); }, []);
(initData () 주위의 괄호에 유의하십시오.
답변
여기에 void 연산자를 사용할 수 있습니다.
대신에:
React.useEffect(() => {
async function fetchData() {
}
fetchData();
}, []);
또는
React.useEffect(() => {
(async function fetchData() {
})()
}, []);
다음과 같이 작성할 수 있습니다.
React.useEffect(() => {
void async function fetchData() {
}();
}, []);
조금 더 깨끗하고 예쁘다.
비동기 효과로 인해 메모리 누수가 발생할 수 있으므로 구성 요소 마운트 해제시 정리를 수행하는 것이 중요합니다. 가져 오기의 경우 다음과 같이 보일 수 있습니다.
function App() {
const [ data, setData ] = React.useState([]);
React.useEffect(() => {
const abortController = new AbortController();
void async function fetchData() {
try {
const url = 'https://jsonplaceholder.typicode.com/todos/1';
const response = await fetch(url, { signal: abortController.signal });
setData(await response.json());
} catch (error) {
console.log('error', error);
}
}();
return () => {
abortController.abort(); // cancel pending fetch request on component unmount
};
}, []);
return <pre>{JSON.stringify(data, null, 2)}</pre>;
}