React 기능 컴포넌트 내에서 비동기/대기 사용
React를 프로젝트에 사용하기 시작한 지 얼마 되지 않아 비동기/대기 기능을 컴포넌트 중 하나에 통합하는 데 어려움을 겪고 있습니다.
비동기 함수는 다음과 같습니다.fetchKeyAWS API 게이트웨이를 통해 서비스 중인 API에서 액세스 키를 가져옵니다.
const fetchKey = async authProps => {
try {
const headers = {
Authorization: authProps.idToken // using Cognito authorizer
};
const response = await axios.post(
"https://MY_ENDPOINT.execute-api.us-east-1.amazonaws.com/v1/",
API_GATEWAY_POST_PAYLOAD_TEMPLATE,
{
headers: headers
}
);
return response.data.access_token;
} catch (e) {
console.log(`Axios request failed! : ${e}`);
return e;
}
};
React의 Material UI 테마를 사용하고 있는데 대시보드 템플릿 중 하나를 사용하기 위해 사용할 수 없게 되었습니다.그러나 대시보드 템플릿은 다음과 같은 기능적인 상태 비저장 구성 요소를 사용합니다.
const Dashboard = props => {
const classes = useStyles();
const token = fetchKey(props.auth);
console.log(token);
return (
... rest of the functional component's code
나의 결과console.log(token)Promise는 예상대로지만 Google Chrome 브라우저의 스크린샷이 다소 모순됩니다. 보류 중입니까, 해결되었습니까?
둘째, 내가 대신 노력한다면token.then((data, error)=> console.log(data, error)), 나는 이해하다undefined두 변수 모두에 대해.이것은 함수가 아직 완료되지 않았음을 나타내며, 따라서 다음 값이 해결되지 않았음을 나타냅니다.data또는error하지만, 만약 제가 이 파일을
const Dashboard = async props => {
const classes = useStyles();
const token = await fetchKey(props.auth);
불만을 강력하게 대응:
> react-dom.development.js:57 Uncaught Invariant Violation: Objects are
> not valid as a React child (found: [object Promise]). If you meant to
> render a collection of children, use an array instead.
> in Dashboard (at App.js:89)
> in Route (at App.js:86)
> in Switch (at App.js:80)
> in div (at App.js:78)
> in Router (created by BrowserRouter)
> in BrowserRouter (at App.js:77)
> in div (at App.js:76)
> in ThemeProvider (at App.js:75)
이 에러 메세지가 어떻게 되어가고 있는지를 이해할 수 있는 충분한 경험이 없습니다.이게 기존의 React 클래스 컴포넌트라면this.setState어떤 상태를 조성하고, 그리고 나서 나의 즐거운 길을 가는 방법.단, 이 기능 컴포넌트에는 해당 옵션이 없습니다.
비동기/대기 로직을 기능하는 React 컴포넌트에 통합하는 방법은 무엇입니까?
편집: 그래서 저는 그냥 바보라고 말할게요.반환되는 실제 응답 개체는 다음과 같습니다.response.data.access_token.그랬다.response.data.Item.access_token도! 그래서 실제 약속은 해결되었지만 결과는 정의되지 않은 것으로 반환되었습니다.
두 가지 사항을 확인해야 합니다.
useEffect와 유사하다componentDidMount그리고.componentDidUpdate를 사용하는 경우,setState여기서 코드 실행을 제한할 필요가 있습니다.componentDidUpdate다음과 같이 합니다.
function Dashboard() {
const [token, setToken] = useState('');
useEffect(() => {
// React advises to declare the async function directly inside useEffect
async function getToken() {
const headers = {
Authorization: authProps.idToken // using Cognito authorizer
};
const response = await axios.post(
"https://MY_ENDPOINT.execute-api.us-east-1.amazonaws.com/v1/",
API_GATEWAY_POST_PAYLOAD_TEMPLATE,
{ headers }
);
const data = await response.json();
setToken(data.access_token);
};
// You need to restrict it at some point
// This is just dummy code and should be replaced by actual
if (!token) {
getToken();
}
}, []);
return (
... rest of the functional component's code
);
}
React Hooks를 사용하면 이제 기능 컴포넌트의 Class 컴포넌트와 동일한 기능을 수행할 수 있습니다.
import { useState, useEffect } from 'react';
const Dashboard = props => {
const classes = useStyles();
const [token, setToken] = useState(null);
useEffect(() => {
async function getToken() {
const token = await fetchKey(props.auth);
setToken(token);
}
getToken();
}, [])
return (
... rest of the functional component's code
// Remember to handle the first render when token is null
이것도 봐주세요.비동기 사용 응답 구성 요소에서 대기 중
구성 요소를 마운트 해제하거나 다른 구성 요소를 사용하여 다시 렌더링할 수 있음props.auth전에fetchKey해결되었습니다.
const Dashboard = props => {
const classes = useStyles();
const [token, setToken] = useState();
const [error, setError] = useState();
const unmountedRef = useRef(false);
useEffect(()=>()=>(unmountedRef.current = true), []);
useEffect(() => {
const effectStale = false; // Don't forget ; on the line before self-invoking functions
(async function() {
const response = await fetchKey(props.auth);
/* Component has been unmounted. Stop to avoid
"Warning: Can't perform a React state update on an unmounted component." */
if(unmountedRef.current) return;
/* Component has re-rendered with different someId value
Stop to avoid updating state with stale response */
if(effectStale) return;
if(response instanceof Error)
setError(response)
else
setToken(response);
})();
return ()=>(effectStale = true);
}, [props.auth]);
if( error )
return <>Error fetching token...{error.toString()}</>
if( ! token )
return <>Fetching token...</>
return //... rest of the functional component's code
또는 Suspend 및 ErrorBoundary를 사용하는 방법도 있습니다.
// render Dashboard with <DashboardSuspend>
const Dashboard = props => {
const classes = useStyles();
const [token, idToken] = props.tokenRef.current || [];
// Fetch token on first render or when props.auth.idToken has changed
if(token === void 0 || idToken !== props.auth.idToken){
/* The thrown promise will be caught by <React.Suspense> which will render
it's fallback until the promise is resolved, then it will attempt
to render the Dashboard again */
throw (async()=>{
const initRef = props.tokenRef.current;
const response = await fetchKey(props.auth);
/* Stop if tokenRef has been updated by another <Dashboard> render,
example with props.auth changed causing a re-render of
<DashboardSuspend> and the first request is slower than the second */
if(initRef !== props.tokenRef.current) return;
props.tokenRef.current = [response, props.auth.idToken];
})()
}
if(props.tokenRef.current instanceof Error){
/* await fetchKey() resolved to an Error, throwing it will be caught by
<ErrorBoundary> which will render it's fallback */
throw props.tokenRef.current
}
return //... rest of the functional component's code
}
const DashboardSuspend = props => {
/* The tokenRef.current will reset to void 0 each time this component is
mounted/re-mounted. To keep the value move useRef higher up in the
hierarchy and pass it down with props or useContext. An alternative
is using an external storage object such as Redux. */
const tokenRef = useRef();
const errorFallback = (error, handleRetry)=>{
const onRetry = ()=>{
// Clear tokenRef otherwise <Dashboard> will throw same error again
tokenRef.current = void 0;
handleRetry();
}
return <>
Error fetching token...{error.toString()}
<Button onClick={onRetry}>Retry</Button>
</>
}
const suspenseFallback = <>Fetching token...</>
return <ErrorBoundary fallback={errorFallback}>
<React.Suspense fallback={suspenseFallback}>
<Dashboard {...props} tokenRef={tokenRef} />
</React.Suspense>
</ErrorBoundary>
}
// Original ErrorBoundary class: https://reactjs.org/docs/error-boundaries.html
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { error: null };
}
static getDerivedStateFromError(error) {
// Update state so the next render will show the fallback UI.
return { error };
}
componentDidCatch(error, errorInfo) {
// You can also log the error to an error reporting service
console.log(error, errorInfo);
}
render() {
if (this.state.error) {
// You can render any custom fallback UI
const handleRetry = () => this.setState({ error: null });
return typeof this.props.fallback === 'function' ? this.props.fallback(this.state.error, handleRetry) : this.props.fallback
}
return this.props.children;
}
}
const token = fetchKey(props.auth);
이렇게 하면 약속이 반환됩니다.데이터를 취득하는 방법 중 하나는 다음과 같습니다.
let token = null;
fetchKey(props.auth).then(result => {
console.log(result)
token = result;
}).catch(e => {
console.log(e)
})
그게 효과가 있으면 알려주세요.
같은 예를 재현했습니다.https://codesandbox.io/embed/quiet-wood-bbygk
언급URL : https://stackoverflow.com/questions/57847626/using-async-await-inside-a-react-functional-component
'source' 카테고리의 다른 글
| 필요한 경우에만 모듈을 동적으로 주입합니다. (0) | 2023.03.08 |
|---|---|
| expo 앱을 로드할 수 없음: 문제가 발생했습니다. (0) | 2023.03.08 |
| html 코드에서 json 파일을 사용하는 방법 (0) | 2023.03.08 |
| 오류 ReferenceError: 개체ID가 정의되지 않았습니다. (0) | 2023.03.08 |
| 고정: Wordpress 사이트 Uncaught TypeError: jQuery(...).live는 JS가 있는 이미지가 표시되지 않도록 하는 함수가 아닙니다. (0) | 2023.03.08 |