useReducer
state ๊ด๋ฆฌ๋ฅผ ๋์์ค๋ค. ๋ ๋ณต์กํ state์ ํนํ ์ ์ฉํ๋ค.
์ฌ๋ฌ state๊ฐ ํจ๊ป ์ํด ์๋ ๊ฒฝ์ฐ
์ฌ๋ฌ state๊ฐ ๊ฐ์ด ๋ฐ๋๊ฑฐ๋ ์๋ก ๊ด๋ จ๋ ๊ฒฝ์ฐ
๐
useState๋ ๊ฑฐ๊ธฐ์์ ์ป์ state๋ ์ฌ์ฉํ๊ฑฐ๋ ๊ด๋ฆฌ๊ฐ ์ด๋ ค์์ง๊ฑฐ๋ ์ค๋ฅ๊ฐ ๋ฐ์ํ๊ธฐ ์ฝ๋ค.
๐
์ด๋ฐ ๊ฒฝ์ฐ useState ๋์ useReducer๋ฅผ ์ธ ์ ์๋ค.
useReducer๋ ๋ ๊ฐ๋ ฅํ state ๊ด๋ฆฌ๊ฐ ํ์ํ ๋ ์ด๋ค. (๊ทธ๋ ๋ค๊ณ ํญ์ ์ฌ์ฉ์ x)
useReducer๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ํญ์ ์ข์๋
๋ค๋ฅธ state๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ๋ state๋ฅผ ์ ๋ฐ์ดํธํ๋ฉด ํ๋์ state๋ก ๋ณํฉํ๋ ๊ฒ๋ ์ข๋ค.
useReducer ์ ๊ตฌ์กฐ
const [state, dispatchFn] = useReducer(reducerFn, initialState, initFn);
state = ์ต์ state ์ค๋ ์ท // ์ด๊ฑด state ๊ด๋ฆฌ ๋งค์ปค๋์ฆ์ด๋ค.
dispatchFn = state ์ค๋ ์ท์ ์ ๋ฐ์ดํธํ ์ ์๊ฒ ํด์ฃผ๋ ํจ์
useState์ ์ฐจ์ด์
์๋ก์ด state ๊ฐ์ ์ค์ ํ๋ ๋์ ์ก์ ์ ๋์คํจ์นํ๋ค.
reducerFn = ๊ทธ ์ก์ ์ useReducer์ ์ฒซ ๋ฒ์งธ ์ธ์๊ฐ ์๋นํ ๊ฑด๋ฐ, reducerFn์ด๋ผ๊ณ ํ๋ค.
์ต์ state ์ค๋ ์ท์ ์๋์ผ๋ก ๊ฐ์ ธ์ค๋ ํจ์์ด๋ค. ์๋๋ฉด ๋ฆฌ์กํธ๊ฐ ์ด ํจ์๋ฅผ ํธ์ถํ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ด๋ค.
๊ทธ๋ฆฌ๊ณ ์ด ํจ์๋ ๋์คํจ์น๋ ์ก์ ์ ๊ฐ์ ธ์จ๋ค. ์๋๋ฉด ๋ฆฌ์กํธ๋ ์ ์ก์ ์ด ๋์คํจ์น๋ ๋๋ง๋ค ์ด ํจ์๋ฅผ ํธ์ถํ๊ธฐ ๋๋ฌธ์ด๋ค. ๊ทธ๋ฌ๋ฉด ์ด ํจ์๋ ๋ฆฌ์กํธ๊ฐ ๊ด๋ฆฌํ๋ ์ต์ ์ state ์ค๋ ์ท์ ๊ฐ์ ธ์จ๋ค. ๊ทธ๋ฆฌ๊ณ ์ด ๋ฆฌ๋์ ํจ์ ์คํ์ ํธ๋ฆฌ๊ฑฐ ํ๋ ๋์คํจ์น๋ ์ก์ ์ ๊ฐ์ ธ์จ๋ค.
& ์๋ก์ด ์ ๋ฐ์ดํธ๋ state๋ฅผ ๋ฐํํ๋ค. (useState ํ ์ ํจ์ ํผ๊ณผ ์ฝ๊ฐ ๋น์ทํจ)
initialState = ์ด๊ธฐ state
initFn = ์ด๊ธฐ state๋ฅผ ์ค์ ํ๊ธฐ ์ํด ์คํํด์ผ ํ๋ ํจ์ (์ด๊ธฐ state๊ฐ ์ข ๋ ๋ณต์กํ ๊ฒฝ์ฐ)
useReducer๋ฅผ ์ ๋ ฅ๊ฐ๊ณผ ์ ํจ์ฑ์ ๊ฒฐํฉํ๊ธฐ ์ํด ์ฌ์ฉํ ์ ์๋ค. (id, ๋น๋ฐ๋ฒํธ)
๊ทธ๋ฆฌ๊ณ ์ ์ฒด ํผ state๋ฅผ ๊ด๋ฆฌํ๋๋ฐ๋ ์ฌ์ฉํ ์ ์๋ค.
์ฆ, ์ ๋ถ ๋ค์ด์๋ ํฐ ํผ state ํ ๊ฐ ๋๋ ์์ state ์ฌ๋ฌ ๊ฐ๋ก ๊ด๋ฆฌํ ์ ์๋ค.
์๋ ์์์์๋ ๊ฐ๋จํ emailState๋ง useReducer๋ก ๊ด๋ฆฌํด๋ณด์!
๊ธฐ์กด ์ฝ๋
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(() => {
console.log("checking form validity!!");
setFormIsValid(
enteredEmail.includes("@") && enteredPassword.trim().length > 6
);
}, 500);
return () => {
console.log("clean up!!!!");
clearTimeout(identifier);
}; // ์ด๊ฑด clean up ํจ์์ด๋ค.
}, [enteredEmail, enteredPassword]);
//์ฌ๊ธฐ์ setFormIsValid()๋ฅผ ์ฌํ๊ฐํ๊ณ ์ ํจ์ฑ ๊ฒ์ฌ state ์ค์ ํจ์๋ฅผ ๋ค์ ์คํํด์ผ ํ๋ค. ์ด๋ฉ์ผ ๋ฐ ๋น๋ฐ๋ฒํธ ๋ณ๊ฒฝ ํธ๋ค๋ฌ์ ๋ชจ๋ ํค ์
๋ ฅ์ ๋ํด์!
//setTimeout ํจ์๋ฅผ ์ฌ์ฉํด์ 500๋ฐ๋ฆฌ์ด ํ์๋ง ์ด ์์
(์ ํจ์ฑ ๊ฒ์ฌ)์ ์ํํ๋ค.
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);
};
useReducer ์ฌ์ฉํ ์ฝ๋
import React, { useState, useEffect, useReducer } from "react";
import Card from "../UI/Card/Card";
import classes from "./Login.module.css";
import Button from "../UI/Button/Button";
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 = (props) => {
// 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,
});
useEffect(() => {
console.log("EFFECT RUNNING");
return () => {
console.log("EFFECT CLEANUP");
};
}, []);
// useEffect(() => {
// const identifier = setTimeout(() => {
// console.log('Checking form validity!');
// setFormIsValid(
// enteredEmail.includes('@') && enteredPassword.trim().length > 6
// );
// }, 500);
// return () => {
// console.log('CLEANUP');
// clearTimeout(identifier);
// };
// }, [enteredEmail, enteredPassword]);
//์ฌ๊ธฐ์ 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();
props.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;