๐Ÿ–ฅFrontEnd/React

๋ฆฌ์•กํŠธ Context ,๋ฆฌ์•กํŠธ ์ปจํ…์ŠคํŠธ์˜ ํ•œ๊ณ„

hellohailie 2022. 7. 26. 00:56

 

๋ฆฌ์•กํŠธ Context๋ž€?

 

๋ฆฌ์•กํŠธ ๋‚ด๋ถ€ state ์ €์žฅ์†Œ๋ฅผ ์ด์šฉํ•ด์„œ ๋ถ€๋ชจ๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•˜์ง€ ์•Š์œผ๋ฉด์„œ ์ €์žฅ์†Œ์—์„œ ์•ก์…˜์„ ํŠธ๋ฆฌ๊ฑฐํ•ด์„œ ๊ด€๋ จ๋œ ์ปดํฌ๋„ŒํŠธ์— ์ง์ ‘ ์ „๋‹ฌ ํ•  ์ˆ˜ ์žˆ๋‹ค. 

 

props๋กœ ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ์ž์‹์—๊ฒŒ ๋„˜๊ฒจ์ฃผ๊ธฐ์—๋Š” ๋„ˆ๋ฌด ๋ณต์žกํ•  ๊ฒƒ ๊ฐ™์„ ๋•Œ ์“ด๋‹ค. 

 

1. components ํŒŒ์ผ ์•ˆ์— store๋ผ๋Š” ํด๋”๋ฅผ ๋งŒ๋“ค๊ณ  ์•„๋ž˜ ํ•จ์ˆ˜๋ฅผ ์จ์ฃผ๊ณ  ๊ผญ export ํ•ด์ฃผ๊ธฐ

//์ „์—ญ state์— ๋Œ€ํ•ด ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ปจํ…์ŠคํŠธ๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋‹ค. ๋ฌผ๋ก  ํ•œ๊ฐœ๋„ ๊ฐ€๋Šฅ
import react from "react";

const AuthContext = React.createContext({
  isLoggedIn: false,
});

export default AuthContext;

 

2. ๊ณต๊ธ‰ํ•˜๊ธฐ = ์ปจํ…์ŠคํŠธ๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•˜๋Š” ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๋ฅผ JSX ์ฝ”๋“œ๋กœ ๊ฐ์‹ธ๊ธฐ

 

์ฐธ๊ณ !! ์ปจํ…์ŠคํŠธ ์ž์ฒด๋Š” ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋˜์ง€ ์•Š๋Š”๋‹ค. ๊ทธ๋ž˜์„œ 

<์ปจํ…์ŠคํŠธ๋ช….Provider> ๋กœ ๊ฐ์‹ธ์ฃผ๊ธฐ

 

//App.js

return (
    //<React.Fragment>
    <AuthContext.Provider>
      <MainHeader isAuthenticated={isLoggedIn} onLogout={logoutHandler} />
      <main>
        {!isLoggedIn && <Login onLogin={loginHandler} />}
        {isLoggedIn && <Home onLogout={logoutHandler} />}
      </main>
    </AuthContext.Provider>
    //</React.Fragment>
  );

 

 

๐Ÿ‘‰ ์•ˆ์— ์žˆ๋Š” ๋ชจ๋“  ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋“ค์ด ํ•ด๋‹น ์ปจํ…์ŠคํŠธ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค. 

 

3. ๋ฆฌ์ŠคํŒ…ํ•˜๊ธฐ // ์†Œ๋น„ํ•˜๊ธฐ

 

๋ฐฉ๋ฒ• 1 (๋ณต์žกํ•ด์„œ ์ž˜ ์•ˆ์“ธ ๊ฒƒ ๊ฐ™์Œ)

์‚ฌ์šฉ์ž๊ฐ€ ์ธ์ฆ๋˜์—ˆ๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ์•Œ๊ณ  ์‹ถ๋‹ค๊ณ  ํ•  ๋•Œ, ํ•ด๋‹น ์ปจํ…์ŠคํŠธ๊ฐ€ ํ•„์š”ํ•œ ๋ชจ๋“  ๊ฒƒ์„ ๊ทธ ์†Œ๋น„์ž๋กœ ๊ฐ์‹ธ์ค€๋‹ค. 

๊ทธ๋ฆฌ๊ณ  ์•„๋ž˜์— {(ctx)=>{ }} ํ•จ์ˆ˜๋ฅผ ์จ์ฃผ๊ณ , return ์œผ๋กœ ๋ชจ๋‘ ๊ฐ์‹ธ์ค€๋‹ค. 

 

๋”๋ณด๊ธฐ
//Nav.js

import React from "react";
import AuthContext from "../store/auth-context";

import classes from "./Navigation.module.css";

const Navigation = (props) => {
  return (
    <AuthContext.Consumer>
      {(ctx) => {
        return (
          <nav className={classes.nav}>
            <ul>
              {ctx.isLoggedIn && (
                <li>
                  <a href='/'>Users</a>
                </li>
              )}
              {props.isLoggedIn && (
                <li>
                  <a href='/'>Admin</a>
                </li>
              )}
              {props.isLoggedIn && (
                <li>
                  <button onClick={props.onLogout}>Logout</button>
                </li>
              )}
            </ul>
          </nav>
        );
      }}
    </AuthContext.Consumer>
  );
};

export default Navigation;

App.js ์—์„œ value ๊ฐ’์œผ๋กœ ์ปจํ…์ŠคํŠธ ๋‚ด์šฉ ๋„ฃ์–ด์ฃผ๊ธฐ

// App.js

return (
    //<React.Fragment>
    <AuthContext.Provider
      value={{
        isLoggedIn: false,
      }}
    >
      <MainHeader isAuthenticated={isLoggedIn} onLogout={logoutHandler} />
      <main>
        {!isLoggedIn && <Login onLogin={loginHandler} />}
        {isLoggedIn && <Home onLogout={logoutHandler} />}
      </main>
    </AuthContext.Provider>
    //</React.Fragment>
  );

 

 

โญ๏ธ๊ณต๊ธ‰์ž ๋ถ€๋ถ„์—์„œ value๋ฅผ state ๋ช…๊ณผ ๊ฐ™์ด ์จ์ค€๋‹ค.

๊ทธ๋Ÿฌ๋ฉด ์ด value ๊ฐ์ฒด๋Š” isLoggedIn์ด ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ๋ฆฌ์•กํŠธ์— ์˜ํ•ด ์—…๋ฐ์ดํŠธ ๋œ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ๊ทธ ์ƒˆ๋กœ์šด ๊ฐ์ฒด, ๊ทธ ์ƒˆ๋กœ์šด ์ปจํ…์ŠคํŠธ ๊ฐ์ฒด๋Š” ๋ชจ๋“  ๋ฆฌ์Šค๋‹ ์ปดํฌ๋„ŒํŠธ๋กœ ์ „๋‹ฌ๋œ๋‹ค.  

 

=> props๋ฅผ ์‚ฌ์šฉํ•  ํ•„์š” ์—†์Œ

return (
    //<React.Fragment>
    <AuthContext.Provider
      value={{
        isLoggedIn: isLoggedIn, // ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ด value ๊ฐ์ฒด๋Š” isLoggedIn์ด ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ๋ฆฌ์•กํŠธ์— ์˜ํ•ด ์—…๋ฐ์ดํŠธ ๋œ๋‹ค.
      }}
    >
      <MainHeader /* isAuthenticated={isLoggedIn} */ onLogout={logoutHandler} />
      <main>
        {!isLoggedIn && <Login onLogin={loginHandler} />}
        {isLoggedIn && <Home onLogout={logoutHandler} />}
      </main>
    </AuthContext.Provider>
    //</React.Fragment>
  );
}

export default App;

 

 

 


3. ๋ฆฌ์ŠคํŒ…ํ•˜๊ธฐ // ์†Œ๋น„ํ•˜๊ธฐ

 

โญ๏ธ๋ฐฉ๋ฒ• 2.โญ๏ธ

useContext ํ›… ์‚ฌ์šฉํ•˜๊ธฐ = ์ปจํ…์ŠคํŠธ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๊ณ , ์ปจํ…์ŠคํŠธ๋ฅผ ํ™œ์šฉํ•˜๊ณ  ๋ฆฌ์Šค๋‹ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•œ๋‹ค. 

์•ฑ ์ „์ฒด ๋˜๋Š” ์ปดํฌ๋„ŒํŠธ ์ „์ฒด state์—๋Š” ์ ํ•ฉํ•˜์ง€๋งŒ, ์ปดํฌ๋„ŒํŠธ ๊ตฌ์„ฑ์„ ๋Œ€์ฒดํ•  ์ˆ˜๋Š” ์—†๋‹ค. 

ex) <Button> ์€ ์ปดํฌ๋„ŒํŠธ ๋ณ„๋กœ ๋‹ค๋ฅธ ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ปจํ…์ŠคํŠธ๊ฐ€ ์•„๋‹Œ props๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค. 

 

์ปดํฌ๋„ŒํŠธ ์•ˆ์— useContext() ๋ฅผ ๋„ฃ๊ณ , ์‚ฌ์šฉํ•˜๋ ค๋Š” ์ปจํ…์ŠคํŠธ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š”  ํฌ์ธํ„ฐ๋ฅผ ๋„ฃ๋Š”๋‹ค. 

 

import React, { useContext } from "react";
import AuthContext from "../store/auth-context";

import classes from "./Navigation.module.css";

const Navigation = (props) => {
  const ctx = useContext(AuthContext);
  return (
    <nav className={classes.nav}>
      <ul>
        {ctx.isLoggedIn && (
          <li>
            <a href='/'>Users</a>
          </li>
        )}
        {ctx.isLoggedIn && (
          <li>
            <a href='/'>Admin</a>
          </li>
        )}
        {ctx.isLoggedIn && (
          <li>
            <button onClick={props.onLogout}>Logout</button>
          </li>
        )}
      </ul>
    </nav>
  );
};

export default Navigation;

 


์ปดํฌ๋„ŒํŠธ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ํ•จ์ˆ˜์—๋„ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•˜๋„๋ก ๋™์  ์ปจํ…์ŠคํŠธ ์„ค์ •ํ•˜๊ธฐ

<AuthContext.Provider
      value={{
        isLoggedIn: isLoggedIn, // ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ด value ๊ฐ์ฒด๋Š” isLoggedIn์ด ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ๋ฆฌ์•กํŠธ์— ์˜ํ•ด ์—…๋ฐ์ดํŠธ ๋œ๋‹ค.
        onLogout: logoutHandler,
      }}
    >

์ด๋Ÿฐ์‹์œผ๋กœ props๋ฅผ ์•ˆ์“ฐ๊ณ  ํ•จ์ˆ˜๋กœ ๋‚ด๋ ค์ค„ ์ˆ˜ ์žˆ๋‹ค. 

 

 


๋” ๋ฆฌํŒฉํ† ๋ง ํ•ด๋ณด๊ธฐ

 

๋”๋ณด๊ธฐ

auth-context.js

 

//auth-context.js
//์ „์—ญ state์— ๋Œ€ํ•ด ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ปจํ…์ŠคํŠธ๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋‹ค. ๋ฌผ๋ก  ํ•œ๊ฐœ๋„ ๊ฐ€๋Šฅ
import React, { useState, useEffect } from "react";

const AuthContext = React.createContext({
  isLoggedIn: false,
  onLogout: () => {},
  onLogin: (email, password) => {},
});

export const AuthContextProvider = (props) => {
  const [isLoggedIn, setIsLoggedIn] = useState(false);

  useEffect(() => {
    const storedUserLoggedInInformation = localStorage.getItem("isLoggedIn");
    if (storedUserLoggedInInformation === "1") {
      setIsLoggedIn(true);
    }
  }, []);

  const logoutHandler = () => {
    localStorage.removeItem("isLoggedIn");
    setIsLoggedIn(false);
  };

  const loginHandler = () => {
    localStorage.setItem("isLoggedIn", "1"); // setItem์€ ๋ธŒ๋ผ์šฐ์ €์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ „์—ญ ๊ฐ์ฒด์ด๋‹ค. ์—ฌ๊ธฐ์— ์•„๋ฌด ์‹๋ณ„์ž๋ฅผ ๋„ฃ์„ ์ˆ˜ ์žˆ๋‹ค. ๋กœ๊ทธ์ธํ–ˆ๋‹ค 1, ์•ˆํ–ˆ๋‹ค 0
    setIsLoggedIn(true);
  };

  return (
    <AuthContext.Provider
      value={{
        isLoggedIn: isLoggedIn, // ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ด value ๊ฐ์ฒด๋Š” isLoggedIn์ด ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ๋ฆฌ์•กํŠธ์— ์˜ํ•ด ์—…๋ฐ์ดํŠธ ๋œ๋‹ค.
        onLogout: logoutHandler,
        onLogin: loginHandler,
      }}
    >
      {props.children}
    </AuthContext.Provider>
  );
};

export default AuthContext;

 

index.js

//index.js
import React from "react";
import ReactDOM from "react-dom";

import "./index.css";
import App from "./App";
import { AuthContextProvider } from "./components/store/auth-context";

ReactDOM.render(
  <AuthContextProvider>
    <App />
  </AuthContextProvider>,
  document.getElementById("root")
);

 

App.js

//App.js

import React, { useContext } from "react";

import Login from "./components/Login/Login";
import Home from "./components/Home/Home";
import MainHeader from "./components/MainHeader/MainHeader";
import AuthContext from "./components/store/auth-context";

function App() {
  const ctx = useContext(AuthContext);

  return (
    <React.Fragment>
      <MainHeader />
      <main>
        {!ctx.isLoggedIn && <Login />}
        {ctx.isLoggedIn && <Home />}
      </main>
    </React.Fragment>
  );
}

export default App;

 

MainHeader.js

// MainHeader.js

import React from "react";

import Navigation from "./Navigation";
import classes from "./MainHeader.module.css";

const MainHeader = (props) => {
  return (
    <header className={classes["main-header"]}>
      <h1>A Typical Page</h1>
      <Navigation onLogout={props.onLogout} />
    </header>
  );
};

export default MainHeader;

 

Navigation.js

// Navigation.js

import React, { useContext } from "react";
import AuthContext from "../store/auth-context";

import classes from "./Navigation.module.css";

const Navigation = () => {
  const ctx = useContext(AuthContext);
  return (
    <nav className={classes.nav}>
      <ul>
        {ctx.isLoggedIn && (
          <li>
            <a href='/'>Users</a>
          </li>
        )}
        {ctx.isLoggedIn && (
          <li>
            <a href='/'>Admin</a>
          </li>
        )}
        {ctx.isLoggedIn && (
          <li>
            <button onClick={ctx.onLogout}>Logout</button>
          </li>
        )}
      </ul>
    </nav>
  );
};

export default Navigation;

 

Home.js

// Home.js

import React, { useContext } from "react";
import AuthContext from "../store/auth-context";
import Button from "../UI/Button/Button";

import Card from "../UI/Card/Card";
import classes from "./Home.module.css";

const Home = () => {
  const authCtx = useContext(AuthContext);
  return (
    <Card className={classes.home}>
      <h1>Welcome back!</h1>
      <Button onClick={authCtx.onLogout}>Logout</Button>
    </Card>
  );
};

export default Home;

 

Login.js

import React, { useState, useEffect, useReducer, useContext } from "react";

import Card from "../UI/Card/Card";
import classes from "./Login.module.css";
import Button from "../UI/Button/Button";
import AuthContext from "../store/auth-context";

const emailReducer = (state, action) => {
  if (action.type === "USER_INPUT") {
    return { value: action.val, isValid: action.val.includes("@") }; // "USER_INPUT" ์•ก์…˜์„ ๋ฐ›์„ ๋•Œ๋งˆ๋‹ค ์—ฌ๊ธฐ์„œ value์™€ isValid๋ฅผ ๋ชจ๋‘ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๊ฒƒ์ด๋‹ค.
  }
  if (action.type === "INPUT_BLUR") {
    return { value: state.value, isValid: state.value.includes("@") };
  }
  return { value: "", isValid: false };
};

const passwordReducer = (state, action) => {
  if (action.type === "USER_INPUT") {
    return { value: action.val, isValid: action.val.trim().length > 6 }; // "USER_INPUT" ์•ก์…˜์„ ๋ฐ›์„ ๋•Œ๋งˆ๋‹ค ์—ฌ๊ธฐ์„œ value์™€ isValid๋ฅผ ๋ชจ๋‘ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๊ฒƒ์ด๋‹ค.
  }
  if (action.type === "INPUT_BLUR") {
    return { value: state.value, isValid: state.value.trim().length > 6 };
  }
  return { value: "", isValid: false };
};

// ๋ฆฌ๋“€์„œ ํ•จ์ˆ˜๋Š” ์ปดํฌ๋„ŒํŠธ ํ•จ์ˆ˜ ๋ฐ–์— ๋งŒ๋“ฌ. ์™œ๋ƒ๋ฉด ๋ฆฌ๋“€์„œ ํ•จ์ˆ˜ ๋‚ด๋ถ€์—์„œ๋Š” ์ปดํฌ๋„ŒํŠธ ํ•จ์ˆ˜ ๋‚ด๋ถ€์—์„œ ๋งŒ๋“ค์–ด์ง„ ์–ด๋–ค ๋ฐ์ดํ„ฐ๋กœ ํ•„์š”ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
// ๋ฆฌ๋“€์„œ ํ•จ์ˆ˜ ๋‚ด๋ถ€์—์„œ ์š”์ฒญ๋˜๊ณ  ์‚ฌ์šฉ๋˜๋Š” ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋Š” ๋ฆฌ์•กํŠธ๊ฐ€ ์ด ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•  ๋•Œ ์ž๋™์œผ๋กœ ์ด ํ•จ์ˆ˜๋กœ ์ „๋‹ฌ๋œ๋‹ค.

const Login = () => {
  // const [enteredEmail, setEnteredEmail] = useState('');
  // const [emailIsValid, setEmailIsValid] = useState();
  // const [enteredPassword, setEnteredPassword] = useState("");
  // const [passwordIsValid, setPasswordIsValid] = useState();
  const [formIsValid, setFormIsValid] = useState(false);

  const [emailState, dispatchEmail] = useReducer(emailReducer, {
    value: "",
    isValid: null,
  });

  const [passwordState, dispatchPassword] = useReducer(passwordReducer, {
    value: "",
    isValid: null,
  });

  const authCtx = useContext(AuthContext);

  useEffect(() => {
    console.log("EFFECT RUNNING");

    return () => {
      console.log("EFFECT CLEANUP");
    };
  }, []);

  const { isValid: emailIsValid } = emailState;
  const { isValid: passwordIsValid } = passwordState;
  //๊ฐ์ฒด ๋””์ŠคํŠธ๋Ÿญ์ฒ˜๋ง
  //๊ฐ’ ํ• ๋‹น์ด ์•„๋‹ˆ๋ผ ๋ณ„์นญ ํ• ๋‹น์ด๋‹ค.
  //useEffect๋ฅผ ๋”์šฑ ์ตœ์ ํ™”ํ•˜๊ณ  ์ดํŽ™ํŠธ๊ฐ€ ๋ถˆํ•„์š”ํ•˜๊ฒŒ ์‹คํ–‰๋˜๋Š” ๊ฒƒ์„ ํ”ผํ•œ๋‹ค.

  useEffect(() => {
    const identifier = setTimeout(() => {
      console.log("Checking form validity!");
      setFormIsValid(emailIsValid && passwordIsValid);
    }, 500);

    return () => {
      console.log("CLEANUP");
      clearTimeout(identifier);
    };
  }, [emailIsValid, passwordIsValid]); // 2. ์ด๋ ‡๊ฒŒ ๋ฐ”๊พธ๋ฉด ๊ฐ’๋งŒ ๋ณ€๊ฒฝ๋˜๊ณ  ์œ ํšจ์„ฑ์€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์œผ๋ฉด ์ด ์ดํŽ™ํŠธ๋Š” ๋‹ค์‹œ ์‹คํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค. (์ผ๋‹จ ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์œ ํšจํ•˜๋ฉด ๋” ํƒ€์ดํ•‘ํ•ด๋„ ์ฝ˜์†”์ฐฝ์— ๋” ์•ˆ๋‚˜ํƒ€๋‚œ๋‹ค. )

  // }, [emailState, passwordState]); // 1. ์˜์กด์„ฑ์€ emailState, passwordState ์ „์ฒด์ด์ง€ ์œ ํšจ์„ฑ ๋ถ€๋ถ„์ด ์•„๋‹ˆ๋‹ค.

  //์—ฌ๊ธฐ์„œ setFormIsValid()๋ฅผ ์žฌํ‰๊ฐ€ํ•˜๊ณ  ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ state ์„ค์ • ํ•จ์ˆ˜๋ฅผ ๋‹ค์‹œ ์‹คํ–‰ํ•ด์•ผ ํ•œ๋‹ค. ์ด๋ฉ”์ผ ๋ฐ ๋น„๋ฐ€๋ฒˆํ˜ธ ๋ณ€๊ฒฝ ํ•ธ๋“ค๋Ÿฌ์˜ ๋ชจ๋“  ํ‚ค ์ž…๋ ฅ์— ๋Œ€ํ•ด์„œ!
  //setTimeout ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด์„œ 500๋ฐ€๋ฆฌ์ดˆ ํ›„์—๋งŒ ์ด ์ž‘์—… (์œ ํšจ์„ฑ ๊ฒ€์‚ฌ)์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.

  const emailChangeHandler = (event) => {
    dispatchEmail({ type: "USER_INPUT", val: event.target.value });

    // setFormIsValid(event.target.value.includes("@") && passwordState.isValid);
  };

  const passwordChangeHandler = (event) => {
    // setEnteredPassword(event.target.value);
    dispatchPassword({ type: "USER_INPUT", val: event.target.value });

    //setFormIsValid(enteredEmail.includes("@") && event.target.value.trim().length > 6)
    //setFormIsValid(emailState.value.includes("@") && event.target.value.trim().length > 6)
    // useReducer๋ฅผ ์ผ๊ธฐ ๋•Œ๋ฌธ์— ์ž…๋ ฅ๋œ ๊ฐ’์„ ์ €์žฅํ•  ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— emailState.value๋ผ๊ณ  ์”€

    // setFormIsValid(emailState.isValid && event.target.value.trim().length > 6);
    // isValid ํ•„๋“œ๊ฐ€ ์žˆ์–ด์„œ ์žฌ๊ฒ€์ฆ ํ•˜๋Š” ๋Œ€์‹  emailState.isValid๊ฐ€ true์ธ์ง€ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ๋‹ค.
  };

  const validateEmailHandler = () => {
    dispatchEmail({ type: "INPUT_BLUR" }); // ์—ฌ๊ธฐ์„œ๋Š” ์ธํ’‹์ด ํฌ์ปค์Šค๋ฅผ ์žƒ์—ˆ๋‹ค๋Š” ๊ฒƒ๋งŒ ์ค‘์š”ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ถ”๊ฐ€ํ•ด์•ผํ•˜๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ์—†๋‹ค.
  };

  const validatePasswordHandler = () => {
    dispatchPassword({ type: "INPUT_BLUR" });
  };

  const submitHandler = (event) => {
    event.preventDefault();
    authCtx.onLogin(emailState.value, passwordState.value);
  };

  return (
    <Card className={classes.login}>
      <form onSubmit={submitHandler}>
        <div
          className={`${classes.control} ${
            emailState.isValid === false ? classes.invalid : ""
          }`}
        >
          <label htmlFor='email'>E-Mail</label>
          <input
            type='email'
            id='email'
            value={emailState.value}
            onChange={emailChangeHandler}
            onBlur={validateEmailHandler}
          />
        </div>
        <div
          className={`${classes.control} ${
            passwordState.isValid === false ? classes.invalid : ""
          }`}
        >
          <label htmlFor='password'>Password</label>
          <input
            type='password'
            id='password'
            value={passwordState.value}
            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;

 

 


๋ฆฌ์•กํŠธ ์ปจํ…์ŠคํŠธ์˜ ํ•œ๊ณ„

๋ณ€๊ฒฝ์ด ์žฆ์€ ๊ฒฝ์šฐ์—๋Š” ๊ทธ๋‹ค์ง€ ์ข‹์€ ๋ฐฉ๋ฒ•์ด ์•„๋‹ˆ๋‹ค. (ex) 1์ดˆ๋งˆ๋‹ค ๋ฐ”๋€œ)

 

๊ทธ๋ ‡๋‹ค๋ฉด ์•ฑ ์ „์ฒด์— ๊ฑธ์ณ ๋˜๋Š” ์ปดํฌ๋„ŒํŠธ ์ „์ฒด์— ๊ฑธ์ณ state๊ฐ€ ์ž์ฃผ ๋ณ€๊ฒฝ๋˜๋Š” ๊ฒฝ์šฐ์—๋Š” ์–ด๋–ป๊ฒŒ ํ•ด์•ผํ• ๊นŒ?

=> ๋ฆฌ๋•์Šค๊ฐ€ ์žˆ๋‹ค!!

 

 

๐Ÿ˜ƒ ์ž˜๋ชป๋œ ๊ฐœ๋… ์ „๋‹ฌ์ด ์žˆ๋‹ค๋ฉด ๋Œ“๊ธ€ ๋ถ€ํƒ๋“œ๋ฆฝ๋‹ˆ๋‹ค. ์ €์˜ ์„ฑ์žฅ์— ํฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค๐Ÿค“