React Hook의 실행 흐름
useState 사용 vs useEffect 사용
useEffect 를 사용 안할시
이 방식의 단점 : 무한 루프를 만들 수도 있습니다.
왜냐하면 이것이 저장되어 있는지 확인하고 저장되어 있으면 이걸 true로 설정합니다. 그리고 state 설정 함수를 호출할 때마다 이 컴포넌트 함수는 다시 실행됩니다. 따라서 이것은 다시 실행되고 1이 나오면 다시 설정, 이런 식으로 계속됩니다.
App.jsx
import React, { useState } from "react";
import Login from "./components/Login/Login";
import Home from "./components/Home/Home";
import MainHeader from "./components/MainHeader/MainHeader";
function App() {
const [isLoggedIn, setIsLoggedIn] = useState(false);
const storedUserLoggedInInformation = localStorage.getItem("isLoggedIn");
if (storedUserLoggedInInformation === "1") {
setIsLoggedIn(true);
}
const loginHandler = (email, password) => {
// 앱이 시작될 때마다 데이터가 유지되었는지 확인 ➡️ 자동으로 사용자를 다시 로그인 (로컬 저장소 사용)
// 사용자가 로그인했다 = "1" || 사용자가 로그인 안했다 = "0"
localStorage.setItem("isLoggedIn", "1");
setIsLoggedIn(true);
};
const logoutHandler = () => {
setIsLoggedIn(false);
};
return (
<React.Fragment>
<MainHeader isAuthenticated={isLoggedIn} onLogout={logoutHandler} />
<main>
{!isLoggedIn && <Login onLogin={loginHandler} />}
{isLoggedIn && <Home onLogout={logoutHandler} />}
</main>
</React.Fragment>
);
}
export default App;
useEffect를 사용하면 언제 실행될지 제어할 수 있습니다.
import React, { useEffect, useState } from "react";
import Login from "./components/Login/Login";
import Home from "./components/Home/Home";
import MainHeader from "./components/MainHeader/MainHeader";
function App() {
const [isLoggedIn, setIsLoggedIn] = useState(false);
// useEffect 사용 : 모든 컴포넌트 재평가 후에 실행된다
useEffect(() => {
const storedUserLoggedInInformation = localStorage.getItem("isLoggedIn");
if (storedUserLoggedInInformation === "1") {
setIsLoggedIn(true);
}
}, []);
const loginHandler = (email, password) => {
localStorage.setItem("isLoggedIn", "1");
setIsLoggedIn(true);
};
const logoutHandler = () => {
setIsLoggedIn(false);
};
return (
<React.Fragment>
<MainHeader isAuthenticated={isLoggedIn} onLogout={logoutHandler} />
<main>
{!isLoggedIn && <Login onLogin={loginHandler} />}
{isLoggedIn && <Home onLogout={logoutHandler} />}
</main>
</React.Fragment>
);
}
export default App;
useState 사용
Login.jsx
import React, { useState } from 'react';
import Card from '../UI/Card/Card';
import classes from './Login.module.css';
import Button from '../UI/Button/Button';
const Login = (props) => {
const [enteredEmail, setEnteredEmail] = useState('');
const [emailIsValid, setEmailIsValid] = useState();
const [enteredPassword, setEnteredPassword] = useState('');
const [passwordIsValid, setPasswordIsValid] = useState();
const [formIsValid, setFormIsValid] = useState(false);
const emailChangeHandler = (event) => {
setEnteredEmail(event.target.value);
setFormIsValid(
event.target.value.includes('@') && enteredPassword.trim().length > 6
);
};
const passwordChangeHandler = (event) => {
setEnteredPassword(event.target.value);
setFormIsValid(
event.target.value.trim().length > 6 && enteredEmail.includes('@')
);
};
const validateEmailHandler = () => {
setEmailIsValid(enteredEmail.includes('@'));
};
const validatePasswordHandler = () => {
setPasswordIsValid(enteredPassword.trim().length > 6);
};
const submitHandler = (event) => {
event.preventDefault();
props.onLogin(enteredEmail, enteredPassword);
};
return (
<Card className={classes.login}>
<form onSubmit={submitHandler}>
<div
className={`${classes.control} ${
emailIsValid === false ? classes.invalid : ''
}`}
>
<label htmlFor="email">E-Mail</label>
<input
type="email"
id="email"
value={enteredEmail}
onChange={emailChangeHandler}
onBlur={validateEmailHandler}
/>
</div>
<div
className={`${classes.control} ${
passwordIsValid === false ? classes.invalid : ''
}`}
>
<label htmlFor="password">Password</label>
<input
type="password"
id="password"
value={enteredPassword}
onChange={passwordChangeHandler}
onBlur={validatePasswordHandler}
/>
</div>
<div className={classes.actions}>
<Button type="submit" className={classes.btn} disabled={!formIsValid}>
Login
</Button>
</div>
</form>
</Card>
);
};
export default Login;
이메일 및 비밀번호 변경 핸들러에서 useEffect를 사용하여 한 곳에서 하나의 로직으로
폼이 유효한지 또는 유효하지 않은지 표시할 수 있습니다 이것은 이메일이나 비밀번호가 변경될 때마다
트리거됩니다 따라서 추가적인 의존성이 필요합니다.
const emailChangeHandler = (event) => {
setEnteredEmail(event.target.value);
setFormIsValid(
event.target.value.includes('@') && enteredPassword.trim().length > 6
);
};
const passwordChangeHandler = (event) => {
setEnteredPassword(event.target.value);
setFormIsValid(
event.target.value.trim().length > 6 && enteredEmail.includes('@')
);
};
useEffect 사용
import React, { useEffect, useState } from "react";
import Card from "../UI/Card/Card";
import classes from "./Login.module.css";
import Button from "../UI/Button/Button";
const Login = (props) => {
const [enteredEmail, setEnteredEmail] = useState("");
const [emailIsValid, setEmailIsValid] = useState();
const [enteredPassword, setEnteredPassword] = useState("");
const [passwordIsValid, setPasswordIsValid] = useState();
const [formIsValid, setFormIsValid] = useState(false);
// enteredEmail 또는 enteredPassword가 바뀔 때마다 다시 실행
useEffect(() => {
setFormIsValid(
enteredEmail.includes("@") && enteredPassword.trim().length > 6
);
}, [enteredEmail, enteredPassword]);
const emailChangeHandler = (event) => {
setEnteredEmail(event.target.value);
};
const passwordChangeHandler = (event) => {
setEnteredPassword(event.target.value);
};
const validateEmailHandler = () => {
setEmailIsValid(enteredEmail.includes("@"));
};
const validatePasswordHandler = () => {
setPasswordIsValid(enteredPassword.trim().length > 6);
};
const submitHandler = (event) => {
event.preventDefault();
props.onLogin(enteredEmail, enteredPassword);
};
return (
<Card className={classes.login}>
<form onSubmit={submitHandler}>
<div
className={`${classes.control} ${
emailIsValid === false ? classes.invalid : ""
}`}
>
<label htmlFor="email">E-Mail</label>
<input
type="email"
id="email"
value={enteredEmail}
onChange={emailChangeHandler}
onBlur={validateEmailHandler}
/>
</div>
<div
className={`${classes.control} ${
passwordIsValid === false ? classes.invalid : ""
}`}
>
<label htmlFor="password">Password</label>
<input
type="password"
id="password"
value={enteredPassword}
onChange={passwordChangeHandler}
onBlur={validatePasswordHandler}
/>
</div>
<div className={classes.actions}>
<Button type="submit" className={classes.btn} disabled={!formIsValid}>
Login
</Button>
</div>
</form>
</Card>
);
};
export default Login;
use Effect에서 cleanUp 함수 사용
키를 입력할 때마다 "check useEffect" 실행. 더 복잡한 작업을 할시 불필요한 네트워크 트래픽을 만들 겁니다.
// enteredEmail 또는 enteredPassword가 바뀔 때마다 다시 실행
useEffect(() => {
console.log("check useEffect");
setFormIsValid(
enteredEmail.includes("@") && enteredPassword.trim().length > 6
);
}, [enteredEmail, enteredPassword]);
예를 들어 사용자가 적극적으로 타이핑하는 동안에는 유효한 이메일 주소인지 확인하고 싶지 않죠
사용자가 타이핑을 멈출 때를 기다립니다 예를 들어 여기에서 타이핑하는데 500밀리초 이상 멈춘다면
그때서야 확인하는 겁니다 사용자가 다 입력한 것 같으니, 유효한지 확인하는 겁니다.
⇒ 사용자 입력을 Debounce 화
🧐 디바운스 유틸리티 함수
/**
* 디바운스 유틸리티 함수
* @param {()=>void} callback 콜백함수
* @param {number} limit 밀리초(ms)
* @returns{()=> void} 디바운스 함수
*/
export function debounce(callback, limit = 300) {
let timeout;
return function (...args) {
console.log(args);
clearTimeout(timeout);
timeout = setTimeout(() => {
callback.apply(this, args);
}, limit);
};
}
cleanUp 적용
// enteredEmail 또는 enteredPassword가 바뀔 때마다 다시 실행
useEffect(() => {
const identifier = setTimeout(() => {
console.log("Checking form validity!");
setFormIsValid(
enteredEmail.includes("@") && enteredPassword.trim().length > 6
);
}, 500);
return () => {
console.log("CLEANUP");
clearTimeout(identifier);
};
}, [enteredEmail, enteredPassword]);
“CLEANUP”이 많이 보이지만 "Checking form validity!"은 한 번만 표시됩니다.
참고
https://ko.reactjs.org/docs/hooks-effect.html
'React' 카테고리의 다른 글
리액트 라우터 outlet 사용하여 중첩된 레이아웃 구성 (0) | 2023.03.09 |
---|---|
Custom Hook 만들기 (0) | 2023.03.01 |
React Portal이란? (0) | 2023.03.01 |
DOM(돔) vs Virtual DOM(가상 돔) (0) | 2023.02.14 |
composition의 개념 ('children prop') (0) | 2022.11.22 |