๋ฆฌ์กํธ 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๊ฐ ์์ฃผ ๋ณ๊ฒฝ๋๋ ๊ฒฝ์ฐ์๋ ์ด๋ป๊ฒ ํด์ผํ ๊น?
=> ๋ฆฌ๋์ค๊ฐ ์๋ค!!
๐ ์๋ชป๋ ๊ฐ๋ ์ ๋ฌ์ด ์๋ค๋ฉด ๋๊ธ ๋ถํ๋๋ฆฝ๋๋ค. ์ ์ ์ฑ์ฅ์ ํฐ ๋์์ด ๋ฉ๋๋ค๐ค
'๐ฅFrontEnd > React' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
๋ฆฌ์กํธ์ ๊ฐ์ DOM, React๊ฐ DOM ํธ๋ฆฌ๋ฅผ ํ์ํ๋ ๋ฐฉ๋ฒ, key ๊ฐ์ ์ค์ ํ๋ ์ด์ (0) | 2022.07.27 |
---|---|
๋ฆฌ์กํธ ํ ์ฌ์ฉ ๊ท์น (0) | 2022.07.26 |
TIL) useState์ useReducer๋ฅผ ์ธ์ ์ธ์ง ๊ตฌ๋ถํ๊ธฐ (0) | 2022.07.25 |
useReducer, useReducer ์ ๊ตฌ์กฐ (0) | 2022.07.25 |
ํด๋ฆฐ์ , clean up ํจ์, ํด๋ฆฐ์ ํจ์๊ฐ ์คํ๋๋ ๊ฒฝ์ฐ, useEffect ์ด์ ๋ฆฌ (0) | 2022.07.24 |