source

React 기능 컴포넌트 내에서 비동기/대기 사용

itover 2023. 3. 8. 21:06
반응형

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

반응형