HomeAboutMeBlogGuest
© 2025 Sejin Cha. All rights reserved.
Built with Next.js, deployed on Vercel
🧚
[1기]최종 프로젝트 데브코스
/
[팀7] 뿡치와 삼촌들 - Devnity
[팀7] 뿡치와 삼촌들 - Devnity
/
📘
프론트엔드 공간
/
🧑‍🎓
Today We Learned
/
라우팅 처리로 인해 언마운트 컴포넌트의 상태를 업데이트 하지 못하는 문제
라우팅 처리로 인해 언마운트 컴포넌트의 상태를 업데이트 하지 못하는 문제
라우팅 처리로 인해 언마운트 컴포넌트의 상태를 업데이트 하지 못하는 문제

라우팅 처리로 인해 언마운트 컴포넌트의 상태를 업데이트 하지 못하는 문제

생성일
Dec 3, 2021 06:44 AM
기록자
Jay Mincheol Cho
해결 여부
해결 여부
속성
React
카테고리
🔍 배경 및 궁금증📢 해결 과정해결에 영감을 준 자료문제 원인해결책해결은 되었지만 아직 잘 이해가 안되는 부분또 다른 해결책 (왜 해결되는지는 아직 모름)

🔍 배경 및 궁금증

로그인 페이지(/login)에서 로그인 버튼을 누르면 history.push로 자동으로 홈 페이지(/)로 이동할 때 아래와 같은 에러가 발생한다
notion image
react_devtools_backend.js:2528 Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function. at LoginForm (http://localhost:8000/main.bundle.js:4817:23) at LoginPage (http://localhost:8000/main.bundle.js:5169:73)

📢 해결 과정

해결에 영감을 준 자료

memory leak으로 검색하던 중 formik에서는 이걸 어떻게 해결하나 궁금해 하고 있었는데 마침 formik 키워드가 담긴 stackoverflow를 봤다
notion image

문제 원인

// useForm const handleSubmit = async (e) => { setIsLoading(true); e.preventDefault(); const newErrors = validate(values); if (Object.keys(newErrors).length === 0) { await onSubmit(values); } setErrors(newErrors); setIsLoading(false); };
  • useForm에서 setErrors와 setIsLoading 하기 전에 onSubmit 함수를 호출한다
  • 문제는 onSubmit 함수는 handleLogin을 전달받는데 handleLogin 내부에서는 history.push로 라우팅을 하여 setErrors와 setIsLoading을 실행하기 전에 라우팅으로 인해 폼 컴포넌트가 unmount 된다는 점이다
// LoginPage const handleLogin = async (loginFields) => { try { const userInfo = await postLogin(loginFields); if (userInfo.token) { fillUserInfo(userInfo); history.push('/'); } } catch (error) { // TODO: alert를 토스트로 교체한다 // eslint-disable-next-line no-alert alert(error.response.data); } };

해결책

  • 따라서 setErrors와 setIsLoading을 모두 마무리한 후에 history.push로 라우팅 처리를 해주면 모든 게 해결된다
  • 그러나 라우팅 처리를 useForm 내부로 가져오는 것은 관심사 분리 측면에서 불합리하다고 판단했다. 왜냐하면 라우팅 처리는 로그인 API(postLogin)가 성공했을 때 실행해야 하는데 useForm은 비동기 로그인 API의 존재를 알지 못하기 때문이다.
  • 그래서 우선 간단하게 처리 할 수 있는 setError를 onSubmit 실행 이전으로 순서를 변경하는 것부터 진행했다
  • 로딩 처리(setIsLoading)는 비동기 로그인 함수가 끝난 이후에 동작해야 하는 것이 분명했으므로 onSubmit 함수 안에서 라우팅 처리 뺐다
  • 그리고 라우팅 처리를 로그인 API의 성공여부를 알 수 있는 컴포넌트 내에서 useEffect를 사용하여 실행하도록 했다
// useForm const handleSubmit = async (e) => { setIsLoading(true); e.preventDefault(); const newErrors = validate(values); setErrors(newErrors); if (Object.keys(newErrors).length === 0) { await onSubmit(values); } setIsLoading(false); };
// LoginPage const { userInfo, fillUserInfo } = useUserInfo(); //... const handleLogin = async (loginFields) => { try { const newUserInfo = await postLogin(loginFields); if (newUserInfo.token) { fillUserInfo(newUserInfo); } } catch (error) { // TODO: alert를 토스트로 교체한다 // eslint-disable-next-line no-alert alert(error.response.data); } }; //... useEffect(() => { if (userInfo.token) { history.push('/'); } }, [history, userInfo.token]);

해결은 되었지만 아직 잘 이해가 안되는 부분

useForm의 setLoading과 useEffect의 실행 순서가 예상과 많이 다르다
notion image
notion image
notion image

또 다른 해결책 (왜 해결되는지는 아직 모름)

import { useState, useEffect } from 'react'; const useForm = ({ initialValues, onSubmit, validate }) => { const [values, setValues] = useState(initialValues); const [errors, setErrors] = useState({}); const [isLoading, setIsLoading] = useState(false); useEffect(() => { return () => {}; }, [isLoading]); const handleChange = (e) => { const { name, value } = e.target; setValues({ ...values, [name]: value }); }; const handleSubmit = async (e) => { setIsLoading(true); e.preventDefault(); const newErrors = validate(values); setErrors(newErrors); if (Object.keys(newErrors).length === 0) { await onSubmit(values); } setIsLoading(false); }; return { values, errors, isLoading, handleChange, handleSubmit, }; }; export default useForm;