๐ ๋ฐฐ๊ฒฝ ๋ฐ ๊ถ๊ธ์ฆ๐ข ํด๊ฒฐ ๊ณผ์ ํด๊ฒฐ์ ์๊ฐ์ ์ค ์๋ฃ๋ฌธ์ ์์ธํด๊ฒฐ์ฑ
ํด๊ฒฐ์ ๋์์ง๋ง ์์ง ์ ์ดํด๊ฐ ์๋๋ ๋ถ๋ถ๋ ๋ค๋ฅธ ํด๊ฒฐ์ฑ
(์ ํด๊ฒฐ๋๋์ง๋ ์์ง ๋ชจ๋ฆ)
๐ ๋ฐฐ๊ฒฝ ๋ฐ ๊ถ๊ธ์ฆ
๋ก๊ทธ์ธ ํ์ด์ง(/login)์์ ๋ก๊ทธ์ธ ๋ฒํผ์ ๋๋ฅด๋ฉด history.push๋ก ์๋์ผ๋ก ํ ํ์ด์ง(/)๋ก ์ด๋ํ ๋ ์๋์ ๊ฐ์ ์๋ฌ๊ฐ ๋ฐ์ํ๋ค

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)
๐ข ํด๊ฒฐ ๊ณผ์
ํด๊ฒฐ์ ์๊ฐ์ ์ค ์๋ฃ

๋ฌธ์ ์์ธ
// 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์ ์คํ ์์๊ฐ ์์๊ณผ ๋ง์ด ๋ค๋ฅด๋ค



๋ ๋ค๋ฅธ ํด๊ฒฐ์ฑ (์ ํด๊ฒฐ๋๋์ง๋ ์์ง ๋ชจ๋ฆ)
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;