setState는 비동기 함수?

junyeon·2024년 3월 28일

오답노트

목록 보기
2/4
post-thumbnail

백엔드와 통신하는 fetch 커스텀 훅을 만든다면 간편하게 통신할 수 있지 않을까?

라는 생각으로 useFetch를 만들어 보았다

usePost

import { useState } from "react";

const usePost = () => {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  const postRequest = async (url, body = {}) => {
    setLoading(true);
    try {
      const response = await fetch(url, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify(body),
      });

      if (!response.ok) {
        throw new Error("네트워크 상태가 올바르지 않습니다.");
      }

      const result = await response.json();
      setData(result);
      setError(null);
    } catch (error) {
      setError(error);
    } finally {
      setLoading(false);
    }
  };

  return { data, loading, error, postRequest };
};

export default usePost;

post에 대한 useFetch 커스텀 훅이고

로그인 테스트에 사용해보았다

아래는 로그인 이벤트 함수이다

submitData

 const submitData = async () => {
    if (!idValueValidation(idValue)) {
      return;
    }
    if (!pwValueValidation(pwValue)) {
      return;
    }
    await postRequest(`${process.env.REACT_APP_API_KEY}/account/auth`, {
      id: idValue,
      pw: pwValue,
    });
    if (loading) {
      return <Div>로그인 중 입니다.</Div>;
    }
    if (error) {
      return <Div>Error{error.message}</Div>;
    }
    if (data && data.token) {
      setCookies("token", data.token, { path: "/" });
      navigate("/");
    }
  };

비동기로 진행하고 통신을 마치면 그 결과를 state에 적용시켜 그 state에 따라 if문이 동작하는 구조로 만들었다

그리고 실행하니

로그인 버튼을 두 번 눌러야 로그인이 되는 것이다

분명 계속 로그인이 안 되는 것이 아니라 두 번을 눌러야 된다는 것은 state의 값이 제대로 반영이 되지 않았다는 뜻 같았다

react state에 대해 찾아보니

setState는 비동기 함수라는 정보를 알게 되었다

두가지 질문

1. 왜 비동기 함수로 사용하는가?

먼저 react Batching에 대해 알아야 한다

리액트에 최적화를 위해 사용되는데 예를 들어

function App() {
  const [count, setCount] = useState(0);
  const handleClick = () => {
    setCount((count) => count + 1);
    setCount((count) => count + 1);
    setCount((count) => count + 1);
  };

  useEffect(() => {
    console.log("count", count);
  }, [count]);

  return <button onClick={handleClick}>+</button>;
}

이 state를 콘솔로 찍어본다면

Count 1,2,3이 아닌

count 3이 나온다

리액트는 단기간에 발생하는 state에 변화에 대해

하나하나 리렌더링을 동작시키는게 아니라

일괄로 처리함으로 써 성능을 최적화 한다

2. 어떻게 해결해야 하는가

간단하다 useEffect를 사용하여 state의 변화에 따라

조건을 주면 된다

즉 한 함수 안에서 if문을 돌리는 것이 아니라

해당 조건문을 따로 때어서 로그인 이벤트 함수가 실행되고 난 뒤

state의 변화에 따라

조건문을 실행하는 것이다

  useEffect(() => {
    if (loading) {
      console.log("로그인 중 입니다.");
    } else if (error) {
      console.error("Error:", error.message);
    } else if (data && data.token) {
      setCookies("token", data.token, { path: "/" });
      navigate("/");
    }
  }, [loading, error, data, setCookies, navigate]);

  const submitData = async () => {
    if (!idValueValidation(idValue)) {
      return;
    }
    if (!pwValueValidation(pwValue)) {
      return;
    }
    await postRequest(`${process.env.REACT_APP_API_KEY}/account/auth`, {
      id: idValue,
      pw: pwValue,
    });
  };

이렇게 바꾼다면 로그인 버튼을 두 번 누르지 않아도

결과값에 따라 state가 변화될 것이고 그것을 useEffect로 다시 렌더링 한다

참고
https://happysisyphe.tistory.com/41

profile
이봐 젊은 친구야 잃어버린 것들은 잃어버린 그 자리에

0개의 댓글