๐Ÿงถ

useReducer

ย 
๐Ÿ€
useReducer - ๋ฐฑ์—…
ย 
ํ…์ŠคํŠธ์—์„œ๋Š” reducer !!!
[๋‹ค์€๋‹˜]
  • ์ดˆ๋ฐ˜ ๋„์ž…๋ถ€๋Š” ์€ํ–‰์œผ๋กœ ๋™์ž‘ ์›๋ฆฌ๋ฅผ ์„ค๋ช… (reducer, action, type, dispatch, etc โ€ฆ)
๐Ÿ“Ž
๋‹ค์€๋‹˜ - useReducer
[์ง€ํ˜•๋‹˜]
  • ์˜ˆ์ œ์ฝ”๋“œ์— ๋Œ€ํ•œ ์„ค๋ช…
โœ๏ธ
์ง€ํ˜•๋‹˜ - useReducer
[์ˆ˜๋ฏผ๋‹˜]
  • ์˜ˆ์ œ์ฝ”๋“œ + alpha (ex. ์™œ reducer๋Š” ์ˆœ์ˆ˜ํ•จ์ˆ˜๋กœ ์ž‘์„ฑํ•ด์•ผํ•˜๋Š”๊ฐ€? ๋“ฑ)
๐Ÿ“Œ
์ˆ˜๋ฏผ๋‹˜ - useReducer
[์ •์Œ๋‹˜]
  • ์•„๋ž˜ ๊ธ€์„ ์ฐธ๊ณ ํ•ด์„œ ์ž‘์„ฑํ•ด์ฃผ์‹œ๋ฉด ๋  ๊ฒƒ ๊ฐ™์•„์š”~ (์ €ํฌ ์˜ˆ์ œ์ฝ”๋“œ์™€์˜ ๋น„๊ต๊ฐ€ ํ•„์š”ํ•˜๋‹ค๋ฉด ์ ์šฉํ•ด์ฃผ์‹œ๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™์•„์š”)
๐Ÿ—’๏ธ
์ •์Œ๋‹˜ - useReducer
๋ฆฌ๋•์Šค ์ž˜ ์“ฐ๊ณ  ๊ณ„์‹œ๋‚˜์š”? - ๋ฆฌ๋””์ฃผ์‹ํšŒ์‚ฌ RIDI Corporation
๋ฆฌ์•กํŠธ ์ƒํƒœ๊ณ„์—๋Š” ๋‹ค์–‘ํ•œ ์ƒํƒœ ๊ด€๋ฆฌ ๊ด€๋ จ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ ์ค‘์—์„œ, ๋ฆฌ๋•์Šค (Redux)๊ฐ€ ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉ๋˜๊ณ  ์žˆ์ง€์š”. ํ†ต๊ณ„์— ๋”ฐ๋ฅด๋ฉด ์•ฝ 48%์˜ ๊ฐœ๋ฐœ์ž๋“ค์ด ๋ฆฌ์•กํŠธ ํ”„๋กœ์ ํŠธ์—์„œ ํ˜„์žฌ ๋ฆฌ๋•์Šค๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค [1]. ๊ทธ๋Ÿฐ๋งŒํผ, ํ˜„์—…์—์„œ ๋ฆฌ๋•์Šค๋ฅผ ์‚ฌ์šฉ์ค‘์ธ ๋ถ„๋“ค์ด ๋งŽ์„ ๊ฒƒ์ด๋ผ๊ณ  ์˜ˆ์ƒํ•ฉ๋‹ˆ๋‹ค. ๋ฆฌ๋•์Šค์˜ ์‚ฌ์šฉ๋ฐฉ์‹์— ์žˆ์–ด์„œ๋Š” ๋”ฑ ์ •ํ•ด์ง„ ๊ทœ์น™์ด ์กด์žฌํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๊ฐœ๋ฐœ์ž๋“ค ๋ชจ๋‘ ์ž์‹ ๋งŒ์˜ ๋ฐฉ์‹์œผ๋กœ ์„œ๋กœ ๋‹ค๋ฅด๊ฒŒ ์‚ฌ์šฉ์„ ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
๋ฆฌ๋•์Šค ์ž˜ ์“ฐ๊ณ  ๊ณ„์‹œ๋‚˜์š”? - ๋ฆฌ๋””์ฃผ์‹ํšŒ์‚ฌ RIDI Corporation๋ฆฌ๋•์Šค ์ž˜ ์“ฐ๊ณ  ๊ณ„์‹œ๋‚˜์š”? - ๋ฆฌ๋””์ฃผ์‹ํšŒ์‚ฌ RIDI Corporation
ย 
ย 
ย 
  1. 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 />);
index.js
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;
App.js
ย 
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;
LoginForm.js
.main { margin-top: 30px; text-align: center; } img { display: block; width: 400px; margin: 0 auto; height: 400px; }
app.css
ย 
  1. ๋กœ๊ทธ์ธ ๊ณผ์ •์„ ์กฐ๊ธˆ ๋” ์„ธ๋ถ„ํ™”ํ•œ ์ฝ”๋“œ
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;
LoginForm.js
ย 
  1. 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;
App.js
ย 
notion imagenotion image
ย 
notion imagenotion image
ย 
notion imagenotion image
ย 
notion imagenotion image
ย 
notion imagenotion image
ย 
ย 
  1. ํŒŒ์ผ ๋ถ„๋ฆฌ์— ๋”ฐ๋ฅธ 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;
Reducer.js
ย 
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;
App.js
ย 
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;
LoginForm.js
  • ํŒŒ์ผ์„ ๋ถ„๋ฆฌํ•˜์˜€์Œ์—๋„ ์ •์ƒ ์ž‘๋™ํ•จ
====== (์œ„์—๊นŒ์ง€ ์ง€ํ˜•๋‹˜์ด ์„ค๋ช…ํ•ด์•ผํ•จ)
  1. 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> );
index.js
ย 
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;
App.js
ย 
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;
LoginForm.js
ย 
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;
Context.js
ย 
  • 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> );
index.js
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;
App.js
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;
LoginForm.js
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;
Reducer.js
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;
Context.js
ย