React

[React] effect 이펙트란?

연어먹고싶음ㅁ 2023. 1. 16. 22:02

Side Effects:  어플리케이션에서 일어나는 모든것

ex) http 리퀘스트 보내기, 브라우저 장소에 저장하는 것 

 

UseEffect 훅 

// arg1. function
// arg2. 의존성 
// 의존성이 변경될때만 arg1. function이 실행됨
useEffect(() => {...}, [dependencies]);

 

useEffect 사용 목적 : 사이드 이펙트를 처리하기 위함. ex) 이메일 주소/비밀번호 입력에 대해 유효성 검사

웹, 브라우저는 다시 로드될 때마다 최근의 데이터가 사라짐

리로드될 때 데이터가 사라지지 않고 유지하기 위해서 useEffect를 사용 

 

useEffet 사용 전, 데이터 저장 방식

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() {
  //2. 데이터를 가져온다 
  const storedUserLoggedInInformation = localStorage.getItem('isLoggedIn');
  // 3. isLoggedIn이 1(로그인을 이미 한 상태)이면, 바로 로그인.. 
  if(storedUserLoggedInInformation === '1'){
    setIsLoggedIn(true);
  }

  const [isLoggedIn, setIsLoggedIn] = useState(false);

  const loginHandler = (email, password) => {
    // We should of course check email and password
    // But it's just a dummy/ demo anyways
    
    //1. 데이터를 저장한다. 
    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;

위와 같이 코드를 작성하면 개발자 도구에서 확인 가능

하지만 위의 코드에 문제점 있음

- 무한 루프를 만들 수 있음 

데이터가 저장되어있는지 확인 > 저장되어있으면 setIsLoggedIn(true) 설정 > state 설정 함수를 호출할때마다 함수 자체가 재 실행됨 (이 과정의 무한루프)

 

useEffet 사용 후, 데이터 저장 방식

import React, { useState, useEffect } 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);

   // 이 함수는 리액트에 의해 실행 , 모든 컴포넌트 재평가 후에 실행 (의존성이 변한 경우만 실행)
   // ex) 앱이 처음 실행될 때 - 의존성이 변경된 것으로 간주 
   // 이 함수 또한 앱이 시작될 때 한번만 실행됨
   //  그 이후로 의존성이 변경되지 않기 때문
  useEffect(() => {
    const storedUserLoggedInInformation = localStorage.getItem("isLoggedIn");
    if (storedUserLoggedInInformation === "1") {
      setIsLoggedIn(true);
    }
  }, []);



  const loginHandler = (email, password) => {
    
    // We should of course check email and password
    // But it's just a dummy/ demo anyways
    localStorage.setItem("isLoggedIn", "1");
    setIsLoggedIn(true);
  };

  const logoutHandler = () => {
    // 로그아웃하면 데이터를 지움
    localStorage.removeItem('isLoggedIn');
    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  함수는 리액트에 의해 실행
모든 컴포넌트 재평가 후에 실행 (의존성이 변한 경우만 실행)
  ex) 앱이 처음 실행될 때 - 의존성이 변경된 것으로 간주
이 함수 또한 앱이 시작될 때 한번만 실행됨. 그 이후로 의존성이 변경되지 않기 때문
 
위 코드의 문제점 : 키가 입력될 때마다 useEffect가 실행됨
 
해결방법 : 일정 시간동안 사용자가 입력하지 않으면, 그 때 키 유효성을 검사함 
 
import React, { useState, useEffect } 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);

  // 모든 로그인 컴포넌트 함수 실행 후에,
  // [] 안의 요소가 컴포넌트 렌더링 주기에서 변경된 경우만 실행
  useEffect(() => {
    const identifier = setTimeout(() => {
      setFormIsValid(
        enteredEmail.target.value.includes("@") &&
          enteredPassword.trim().length > 6
      );
    }, 500);

    // 클린업 function
    // useEffect가 다음번에 이 함수를 실행하기 전 이 함수가 실행 됨.
    // 이펙트를 특정한 컴포넌트가 DOM 에서 마운트 해재할 때마다 > 컴포넌트가 재사용
    // 모든 새로운 사이드 이펙트 함수가 실행되기 전에 , 컴포넌트가 실행되기 전에
    // 처음 로드 될떄는 실행 안됨.
    return () => {
      // 새로운 타이머를 설정하기 전에 마지막 타이머를 지움
      clearTimeout(identifier);
    };
  }, [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;