ย
useReducer - ๋ฐฑ์
ย
ํ
์คํธ์์๋ reducer !!!
[๋ค์๋]
- ์ด๋ฐ ๋์ ๋ถ๋ ์ํ์ผ๋ก ๋์ ์๋ฆฌ๋ฅผ ์ค๋ช (reducer, action, type, dispatch, etc โฆ)
[์งํ๋]
- ์์ ์ฝ๋์ ๋ํ ์ค๋ช
[์๋ฏผ๋]
- ์์ ์ฝ๋ + alpha (ex. ์ reducer๋ ์์ํจ์๋ก ์์ฑํด์ผํ๋๊ฐ? ๋ฑ)
[์ ์๋]
- ์๋ ๊ธ์ ์ฐธ๊ณ ํด์ ์์ฑํด์ฃผ์๋ฉด ๋ ๊ฒ ๊ฐ์์~ (์ ํฌ ์์ ์ฝ๋์์ ๋น๊ต๊ฐ ํ์ํ๋ค๋ฉด ์ ์ฉํด์ฃผ์๋ฉด ์ข์ ๊ฒ ๊ฐ์์)
ย
ย
ย
- useState๋ฅผ ์ด์ฉํ ๋ก๊ทธ์ธ ๊ธฐ๋ฅ ๊ตฌํ ์์ ์ฝ๋
import React from "react"; import { createRoot } from "react-dom/client"; import App from "./App"; const container = document.getElementById("root"); const root = createRoot(container); root.render(<App />);
import { useState } from "react"; import LoginForm from "./components/LoginForm"; function App() { const [isLogin, setIsLogin] = useState(false); return ( <div> {isLogin ? ( <div> <strong>ํ์ํฉ๋๋ค~ ๋ผ์ด์บฃ๋!</strong> <img src="https://paullab.co.kr/images/message_licat.png" alt="๋ผ์ด์บฃ" /> <button onClick={() => setIsLogin(!isLogin)}>๋ก๊ทธ์์</button> </div> ) : ( <LoginForm setIsLogin={setIsLogin} /> )} </div> ); } export default App;
ย
import { useState } from "react"; function LoginForm({ setIsLogin }) { const [id, setId] = useState(""); const [password, setPassword] = useState(""); const [message, setMessage] = useState(""); const handleLoginForm = (event) => { event.preventDefault(); if (id === "licat" && password === "weniv!!") { setIsLogin(true); setMessage("๋ก๊ทธ์ธ ์ฑ๊ณต!"); } else { setMessage("๋ก๊ทธ์ธ ์คํจ!"); } }; return ( <form action="" onSubmit={handleLoginForm}> <label>ID </label> <input type="text" placeholder="์์ด๋๋ฅผ ์ ๋ ฅํด์ฃผ์ธ์" onChange={(event) => setId(event.target.value)} /> <br /> <br /> <label>Password </label> <input type="password" placeholder="๋น๋ฐ๋ฒํธ๋ฅผ ์ ๋ ฅํด์ฃผ์ธ์" onChange={(event) => setPassword(event.target.value)} /> <br /> <br /> <button>๋ก๊ทธ์ธ ํ๊ธฐ</button> <br /> <p>{message}</p> </form> ); } export default LoginForm;
.main { margin-top: 30px; text-align: center; } img { display: block; width: 400px; margin: 0 auto; height: 400px; }
ย
- ๋ก๊ทธ์ธ ๊ณผ์ ์ ์กฐ๊ธ ๋ ์ธ๋ถํํ ์ฝ๋
import { useState } from "react"; function LoginForm({ setIsLogin }) { const [id, setId] = useState(""); const [password, setPassword] = useState(""); const [message, setMessage] = useState(""); const handleLoginForm = (event) => { event.preventDefault(); if (id === "licat" && password === "weniv!!") { setIsLogin(true); setMessage("๋ก๊ทธ์ธ ์ฑ๊ณต!"); } else if (id === "licat" && password !== "weniv!!") { setMessage("๋น๋ฐ๋ฒํธ๋ฅผ ๋ค์ ํ๋ฒ ๊ธฐ์ตํด๋ณด์ธ์~"); } else if (id !== "licat" && password === "weniv!!") { setMessage("์์ด๋๋ฅผ ๋ค์ ํ๋ฒ ๊ธฐ์ตํด๋ณด์ธ์~"); } else { setMessage("์์ด๋๋ ๋น๋ฐ๋ฒํธ๊ฐ ๋ชจ๋ ํ๋ ธ์ด์~ ใ ใ "); } }; return ( <form action="" onSubmit={handleLoginForm}> <label>ID </label> <input type="text" placeholder="์์ด๋๋ฅผ ์ ๋ ฅํด์ฃผ์ธ์" onChange={(event) => setId(event.target.value)} /> <br /> <br /> <label>Password </label> <input type="password" placeholder="๋น๋ฐ๋ฒํธ๋ฅผ ์ ๋ ฅํด์ฃผ์ธ์" onChange={(event) => setPassword(event.target.value)} /> <br /> <br /> <button>๋ก๊ทธ์ธ ํ๊ธฐ</button> <br /> <p>{message}</p> </form> ); } export default LoginForm;
ย
- useReducer ์ ์ฉ - App.js ํ๋์ ๋ชจ๋ ์์ฑ
- console.log๋ก ์ฐํ๋ action.type๊ณผ state๊ฐ ์ด์ ์ํ์ ๊ฒ๋ค์์ ์ ์!
import { useState, useReducer } from "react"; import "./app.css"; const reducer = (state, action) => { console.log("old State: ", action.type, state); switch (action.type) { case "LOGIN_SUCCESS": console.log(action.type, state); return { user: action.payload, isLogin: true, message: "๋ก๊ทธ์ธ ์ฑ๊ณต!", }; case "MISS_ID": console.log(action.type, state); return { isLogin: false, message: "์์ด๋๋ฅผ ๋ค์ ํ๋ฒ ๊ธฐ์ตํด๋ณด์ธ์~", }; case "MISS_PASSWORD": console.log(action.type, state); return { isLogin: false, message: "๋น๋ฐ๋ฒํธ๋ฅผ ๋ค์ ํ๋ฒ ๊ธฐ์ตํด๋ณด์ธ์~", }; case "LOGIN_FAILURE": console.log(action.type, state); return { isLogin: false, message: "์์ด๋๋ ๋น๋ฐ๋ฒํธ๊ฐ ๋ชจ๋ ํ๋ ธ์ด์~ ใ ใ ", }; case "LOGOUT": console.log(action.type, state); return { isLogin: false, message: "๋ก๊ทธ์์!", }; default: return state; } }; function App() { const [id, setId] = useState(''); const [password, setPassword] = useState(''); const userInfo = { id: "licat", password: "weniv!!" }; const [state, dispatch] = useReducer(Reducer, { isLogin: false, message: "" }); const handleLoginForm = (event) => { event.preventDefault(); if (id === userInfo.id && password === userInfo.password) { dispatch({ type: "LOGIN_SUCCESS", payload: userInfo }); } else if (id !== userInfo.id && password === userInfo.password) { dispatch({ type: "MISS_ID" }); } else if (id === userInfo.id && password !== userInfo.password) { dispatch({ type: "MISS_PASSWORD" }); } else { dispatch({ type: "LOGIN_FAILURE" }); } }; return ( <div className="main"> {state.isLogin ? ( <> <strong>ํ์ํฉ๋๋ค~ ๋ผ์ด์บฃ๋!</strong> <img src="https://paullab.co.kr/images/message_licat.png" alt="๋ผ์ด์บฃ" /> <button onClick={() => dispatch({ type: "LOGOUT" })}>๋ก๊ทธ์์</button> </> ) : ( <form action="" onSubmit={handleLoginForm}> <label>ID</label> <input type="text" placeholder="์์ด๋๋ฅผ ์ ๋ ฅํด์ฃผ์ธ์" onChange={(event) => setId(event.target.value)} /> <br /> <br /> <label>Password</label> <input type="password" placeholder="๋น๋ฐ๋ฒํธ๋ฅผ ์ ๋ ฅํด์ฃผ์ธ์" onChange={(event) => setPassword(event.target.value)} /> <br /> <br /> <button>๋ก๊ทธ์ธ ํ๊ธฐ</button> <br /> <p>{state.message}</p> </form> )} </div> ); } export default App;
ย
ย
ย
ย
ย
ย
ย
- ํ์ผ ๋ถ๋ฆฌ์ ๋ฐ๋ฅธ props drilling ๋ฐ์
ย
const Reducer = (state, action) => { console.log("old State: ", action.type, state); switch (action.type) { case "LOGIN_SUCCESS": console.log(action.type, state); return { user: action.payload, isLogin: true, message: "๋ก๊ทธ์ธ ์ฑ๊ณต!", }; case "MISS_ID": console.log(action.type, state); return { isLogin: false, message: "์์ด๋๋ฅผ ๋ค์ ํ๋ฒ ๊ธฐ์ตํด๋ณด์ธ์~", }; case "MISS_PASSWORD": console.log(action.type, state); return { isLogin: false, message: "๋น๋ฐ๋ฒํธ๋ฅผ ๋ค์ ํ๋ฒ ๊ธฐ์ตํด๋ณด์ธ์~", }; case "LOGIN_FAILURE": console.log(action.type, state); return { isLogin: false, message: "์์ด๋๋ ๋น๋ฐ๋ฒํธ๊ฐ ๋ชจ๋ ํ๋ ธ์ด์~ ใ ใ ", }; case "LOGOUT": console.log(action.type, state); return { isLogin: false, message: "๋ก๊ทธ์์!", }; default: return state; } }; export default Reducer;
ย
import { useReducer } from "react"; import Reducer from "./context/Reducer"; import LoginForm from "./components/LoginForm"; import "./app.css"; function App() { const [state, dispatch] = useReducer(Reducer, { isLogin: false, message: "" }); return ( <div className="main"> {state.isLogin ? ( <> <strong>ํ์ํฉ๋๋ค~ ๋ผ์ด์บฃ๋!</strong> <img src="https://paullab.co.kr/images/message_licat.png" alt="๋ผ์ด์บฃ" /> <button onClick={() => dispatch({ type: "LOGOUT" })}>๋ก๊ทธ์์</button> </> ) : ( <LoginForm state={state} dispatch={dispatch} /> )} </div> ); } export default App;
ย
import { useState } from "react"; function LoginForm({ state, dispatch }) { const [id, setId] = useState(''); const [password, setPassword] = useState(''); const userInfo = { id: "licat", password: "weniv!!" }; const handleLoginForm = (event) => { event.preventDefault(); if (id === userInfo.id && password === userInfo.password) { dispatch({ type: "LOGIN_SUCCESS", payload: userInfo }); } else if (id !== userInfo.id && password === userInfo.password) { dispatch({ type: "MISS_ID" }); } else if (id === userInfo.id && password !== userInfo.password) { dispatch({ type: "MISS_PASSWORD" }); } else { dispatch({ type: "LOGIN_FAILURE" }); } }; return ( <form action="" onSubmit={handleLoginForm}> <label>ID</label> <input type="text" placeholder="์์ด๋๋ฅผ ์ ๋ ฅํด์ฃผ์ธ์" onChange={(event) => setId(event.target.value)} /> <br /> <br /> <label>Password</label> <input type="password" placeholder="๋น๋ฐ๋ฒํธ๋ฅผ ์ ๋ ฅํด์ฃผ์ธ์" onChange={(event) => setPassword(event.target.value)} /> <br /> <br /> <button>๋ก๊ทธ์ธ ํ๊ธฐ</button> <br /> <p>{state.message}</p> </form> ); } export default LoginForm;
- ํ์ผ์ ๋ถ๋ฆฌํ์์์๋ ์ ์ ์๋ํจ
====== (์์๊น์ง ์งํ๋์ด ์ค๋ช
ํด์ผํจ)
- useContext ์ฌ์ฉ
import React from "react"; import { createRoot } from "react-dom/client"; import App from "./App"; import { ContextProvider } from "./context/Context"; const container = document.getElementById("root"); const root = createRoot(container); root.render( <ContextProvider> <App /> </ContextProvider> );
ย
import { useContext } from "react"; import LoginForm from "./components/LoginForm"; import Context from "./context/Context"; import "./app.css"; function App() { const { state, dispatch } = useContext(Context); return ( <div className="main"> {state.isLogin ? ( <> <strong>ํ์ํฉ๋๋ค~ ๋ผ์ด์บฃ๋!</strong> <img src="https://paullab.co.kr/images/message_licat.png" alt="๋ผ์ด์บฃ" /> <button onClick={() => dispatch({ type: "LOGOUT" })}>๋ก๊ทธ์์</button> </> ) : ( <LoginForm /> )} </div> ); } export default App;
ย
import { useState, useContext } from "react"; import Context from "../context/Context"; function LoginForm() { const [id, setId] = useState(''); const [password, setPassword] = useState(''); const { state, dispatch } = useContext(Context); const userInfo = { id: "licat", password: "weniv!!" }; const handleLoginForm = (event) => { event.preventDefault(); if (id === userInfo.id && password === userInfo.password) { dispatch({ type: "LOGIN_SUCCESS", payload: userInfo }); } else if (id !== userInfo.id && password === userInfo.password) { dispatch({ type: "MISS_ID" }); } else if (id === userInfo.id && password !== userInfo.password) { dispatch({ type: "MISS_PASSWORD" }); } else { dispatch({ type: "LOGIN_FAILURE" }); } }; return ( <form action="" onSubmit={handleLoginForm}> <label>ID</label> <input type="text" placeholder="์์ด๋๋ฅผ ์ ๋ ฅํด์ฃผ์ธ์" onChange={(event) => setId(event.target.value)}/> <br /> <br /> <label>Password</label> <input type="password" placeholder="๋น๋ฐ๋ฒํธ๋ฅผ ์ ๋ ฅํด์ฃผ์ธ์" onChange={(event) => setPassword(event.target.value)}/> <br /> <br /> <button>๋ก๊ทธ์ธ ํ๊ธฐ</button> <br /> <p>{state.message}</p> </form> ); } export default LoginForm;
ย
import { createContext, useReducer } from "react"; import Reducer from "./Reducer"; const INITIAL_STATE = { isLogin: false, message: "" }; export const Context = createContext(INITIAL_STATE); export const ContextProvider = ({ children }) => { const [state, dispatch] = useReducer(Reducer, INITIAL_STATE); return ( <Context.Provider value={{ state, dispatch, }} > {children} </Context.Provider> ); }; export default Context;
ย
- props๋ก LoginForm.js์๊ฒ state, dispatch๋ฅผ ์ ๋ฌํ์ง ์์๋ ์ ๊ทผ์ด ๊ฐ๋ฅํ๋ค.
ย
ย
์ต์ข ์ฝ๋
import React from "react"; import { createRoot } from "react-dom/client"; import App from "./App"; import { ContextProvider } from "./context/Context"; const container = document.getElementById("root"); const root = createRoot(container); root.render( <ContextProvider> <App /> </ContextProvider> );
import { useContext } from "react"; import LoginForm from "./components/LoginForm"; import Context from "./context/Context"; import "./app.css"; function App() { const { state, dispatch } = useContext(Context); return ( <div className="main"> {state.isLogin ? ( <> <strong>ํ์ํฉ๋๋ค~ ๋ผ์ด์บฃ๋!</strong> <img src="https://paullab.co.kr/images/message_licat.png" alt="๋ผ์ด์บฃ" /> <button onClick={() => dispatch({ type: "LOGOUT" })}>๋ก๊ทธ์์</button> </> ) : ( <LoginForm /> )} </div> ); } export default App;
import { useState, useContext } from "react"; import Context from "../context/Context"; function LoginForm() { const [id, setId] = useState(''); const [password, setPassword] = useState(''); const { state, dispatch } = useContext(Context); const userInfo = { id: "licat", password: "weniv!!" }; const handleLoginForm = (event) => { event.preventDefault(); if (id === userInfo.id && password === userInfo.password) { dispatch({ type: "LOGIN_SUCCESS", payload: userInfo }); } else if (id !== userInfo.id && password === userInfo.password) { dispatch({ type: "MISS_ID" }); } else if (id === userInfo.id && password !== userInfo.password) { dispatch({ type: "MISS_PASSWORD" }); } else { dispatch({ type: "LOGIN_FAILURE" }); } }; return ( <form action="" onSubmit={handleLoginForm}> <label>ID</label> <input type="text" placeholder="์์ด๋๋ฅผ ์ ๋ ฅํด์ฃผ์ธ์" onChange={(event) => setId(event.target.value)}/> <br /> <br /> <label>Password</label> <input type="password" placeholder="๋น๋ฐ๋ฒํธ๋ฅผ ์ ๋ ฅํด์ฃผ์ธ์" onChange={(event) => setPassword(event.target.value)}/> <br /> <br /> <button>๋ก๊ทธ์ธ ํ๊ธฐ</button> <br /> <p>{state.message}</p> </form> ); } export default LoginForm;
const Reducer = (state, action) => { switch (action.type) { case "LOGIN_SUCCESS": return { ...state, user: action.payload, isLogin: true, message: "๋ก๊ทธ์ธ ์ฑ๊ณต!", }; case "MISS_ID": return { ...state, isLogin: false, message: "์์ด๋๋ฅผ ๋ค์ ํ๋ฒ ๊ธฐ์ตํด๋ณด์ธ์~", }; case "MISS_PASSWORD": return { ...state, isLogin: false, message: "๋น๋ฐ๋ฒํธ๋ฅผ ๋ค์ ํ๋ฒ ๊ธฐ์ตํด๋ณด์ธ์~", }; case "LOGIN_FAILURE": return { ...state, isLogin: false, message: "์์ด๋๋ ๋น๋ฐ๋ฒํธ๊ฐ ๋ชจ๋ ํ๋ ธ์ด์~ ใ ใ ", }; case "LOGOUT": return { ...state, isLogin: false, message: "๋ก๊ทธ์์!", }; default: return { ...state }; } }; export default Reducer;
import { createContext, useReducer } from "react"; import Reducer from "./Reducer"; const INITIAL_STATE = { isLogin: false, message: "" }; export const Context = createContext(INITIAL_STATE); export const ContextProvider = ({ children }) => { const [state, dispatch] = useReducer(Reducer, INITIAL_STATE); return ( <Context.Provider value={{ state, dispatch, }} > {children} </Context.Provider> ); }; export default Context;
ย