yamoo9 / likelion-FEQA

질문/답변 — 프론트엔드 스쿨, 멋사
29 stars 9 forks source link

[LAB-17] ajax통신의 결과값을 setState 적용하는데 state 변환시기가 한박자 느린 이슈 #282

Closed hyunwlee-dev closed 1 year ago

hyunwlee-dev commented 1 year ago

질문 작성자

이현우

문제 상황

✓ 만든 함수

✓ 설명 시작

Firestore Database안에

스크린샷 2023-04-07 오전 12 55 53

toto1234@naturekarly.com값이 존재한 상황이구요


스크린샷 2023-04-07 오전 12 56 30

현재는 테스트를 위해 하드코딩으로 직접 아이디를 넣어놓은 상황입니다.


스크린샷 2023-04-07 오전 1 02 07

문제의 주인공 setData입니다. data 결과값이 한박자 늦게 나타나기 때문입니다.


테스트 결과를 보여드리겠습니다!

스크린샷 2023-04-07 오전 1 08 37 스크린샷 2023-04-07 오전 1 06 42

question1

excute를 했지만 결과값이 setState에 들어가는 박자가 한박자 늦어지는 것을 확인할 수 있었습니다.

눌렀을때 바로 data state값이 단번에 잘 나오는 방법을 잘 모르겠습니다!

프로젝트 저장소 URL

.env

VITE_API_KEY=AIzaSyBELJiqqXbAp9Bs8F_CW7dxaDGHSNP9c0A
VITE_AUTH_DOMAIN=naturekarly.firebaseapp.com
VITE_PROJECT_ID=naturekarly
VITE_MESSAGE_SENDER_ID=866927372863
VITE_STORAGE_BUCKET=naturekarly.appspot.com
VITE_APP_ID=1:866927372863:web:7b2a5d7b1522391bd6268a
hyunwlee-dev commented 1 year ago

오늘 야무쌤의 useSignUp 훅을 보니 왜 return으로도 data를 넘겼주셨는지 알게되었습니다.

스크린샷 2023-04-07 오전 11 03 58

https://usehooks.com/ 여기서, useAsync는 결과값을 state로만 다루는데 이러한 예제에서는 저와 같은 이슈가 발생하면 어떻게 해결하려고 return으로도 data를 넘겨주지 않았나요? ajax 결과를 state로 다루면서도 시간차를 극복하는 방법이 따로 있나요?

스크린샷 2023-04-07 오전 11 14 49
yamoo9 commented 1 year ago

질문

useAsync 훅은 결과 값을 state로만 다루는데 이러한 예제는 저와 같은 이슈(지연된 상태 업데이트)가 발생하면 어떻게 해결하려고 return으로 data를 넘겨주지 않았나요? AJAX 결과를 state로 다루면서도 시간 차를 극복하는 방법이 따로 있나요?

답변

useAsync 훅 코드를 찬찬히 살펴봅시다.

myFunction

먼저 useAsync 훅 함수를 테스트 하기 위한 비동기 함수 코드입니다. 50% 확률로 성공 또는 실패하는 Promise를 반환합니다.

const myFunction = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const rnd = Math.random() * 10;
      rnd <= 5
        ? resolve('성공적으로 제출됨 🙌')
        : reject('앗! 오류가 났네요 😞');
    }, 2000);
  });
};

App

App 컴포넌트 내부에서 useAsync 훅을 실행하여 상태(status), 값(value), 오류(error), 실행(excute) 함수를 추출합니다. 첫번째 인자로 myFunction 비동기 함수를, 두번째 인자로 즉시 실행하지 않도록 설정합니다.

예제에서 useAsync 훅의 두번째 인자로 false를 설정했기 때문에 execute 함수는 즉시 실행되지 않습니다. execute 함수는 사용자가 button 요소를 클릭할 때 실행됩니다.

unction App() {

  // useAsync 훅 실행 시 인자를 전달합니다.
  // - myFunction : 비동기 처리 함수 (50% 확률로 성공 또는 실패 됨)
  // - immediate : 즉시 실행 여부
  const { execute, status, value, error } = useAsync(myFunction, false);

  return (
    <div>
      {status === 'idle' && <div>버튼을 클릭해 여행을 시작하세요!</div>}
      {status === 'success' && <div>{value}</div>}
      {status === 'error' && <div>{error}</div>}
      <button onClick={execute} disabled={status === 'pending'}>
        {status !== 'pending' ? '클릭' : '로딩...'}
      </button>
    </div>
  );
}

useAsync

useAsync 훅은 execute, status, value, error를 포함한 객체를 반환합니다. 이 훅은 비동기 함수(필수)와 즉시 실행 여부(옵션)를 인자로 전달 받습니다.

execute 함수는 비동기 함수(asyncFunction) 인자가 변경된 경우에만 다시 메모리를 시도합니다. 그렇지 않은 경우 메모된 함수를 반환합니다.

execute 함수 내부 코드를 살펴보면 asyncFunction(Promise 반환)이 실행된 이후, 성공 또는 실패 여부에 따라 then, catch 구문이 실행되는 것을 확인할 수 있습니다.

윗 부분이 질문의 "state로 다루면서도 시간 차를 극복하는 방법이 따로 있나요?"에 대한 답변입니다. (⭐️ 핵심)

기본적으로 useAsync 훅은 즉시 execute 함수를 실행하지만, 사용자가 immediate 매개변수 값을 false로 설정한 경우 이벤트 핸들링(예: click)에 의해 execute 함수가 실행되도록 구성할 수 있습니다.

const useAsync = (asyncFunction, immediate = true) => {
  const [status, setStatus] = useState('idle');
  const [value, setValue] = useState(null);
  const [error, setError] = useState(null);

  // execute 함수는 asyncFunction을 래핑하고 
  // 보류(pending), 값(value) 및 오류(error)에 대한 설정 상태를 처리합니다.
  const execute = useCallback(
    () => {
      setStatus('pending');
      setValue(null);
      setError(null);
      return asyncFunction()
        .then((response) => {
          setValue(response);
          setStatus('success');
        })
        .catch((error) => {
          setError(error);
          setStatus('error');
        });
    },
    // useCallback은 asyncFunction이 변경되는 경우에만 다시 메모리 합니다.
    [asyncFunction],
  );

  useEffect(() => {
    // immediate 값이 true인 경우, execute 함수를 즉시 실행합니다.
    // 그렇지 않으면 onClick 핸들러를 통해 나중에 execute 함수를 실행할 수 있습니다.
    if (immediate) {
      execute();
    }
  }, [execute, immediate]);

  return { execute, status, value, error };
};