[TIL] 0627 | React with Redux, Next.js, TypeScript

TeasanΒ·2022λ…„ 6μ›” 27일
0

TIL

λͺ©λ‘ 보기
18/36
post-thumbnail
post-custom-banner

λͺ©μ°¨

  • Applying Our Hook & Knowledge To A New Form
  • useReducer μ μš©ν•˜κΈ°

✧ Applying Our Hook & Knowledge To A New Form

μΆ”κ°€μ μœΌλ‘œ μˆ˜μ •ν•œ λΆ€λΆ„

  • useInput에 μ „λ‹¬ν•˜λŠ” ν•¨μˆ˜ 둜직 λ”°λ‘œ μ „μ—­ λ³€μˆ˜λ‘œ λΉΌκΈ°.
const isNotEmpty = (value) => value.trim() !== "";
const isEmail = (value) => value.includes("@") && isNotEmpty;

// Name
const {
  value: enteredLastName,
  isValid: enteredLastNameIsValid,
  hasError: lastNameInputHasError,
  valueChangeHandler: lastNameChangeHandler,
  inputBlurHandler: lastNameInputBlurHandler,
  reset: resetLastName,
} = useInput(isNotEmpty);

// email
const {
  value: enteredEmail,
  isValid: enteredEmailIsValid,
  hasError: emailInputHasError,
  valueChangeHandler: emailChangeHandler,
  inputBlurHandler: emailInputBlurHandler,
  reset: resetEmail,
} = useInput(isEmail);
  • μ‚Όν•­ μ—°μ‚°μžκ°€ μ•„λ‹ˆλΌ &&둜 둜직 κ°„κ²°ν•˜κ²Œ 쀄이기
// {
//   firstNameInputHasError ? (
//     <p className="error-text">First Name 이 λΉ„μ–΄μžˆμŠ΅λ‹ˆλ‹€.</p>
//   ) : (
//     ""
//   );
// }

{
  firstNameInputHasError && (
    <p className="error-text">First Name 이 λΉ„μ–΄μžˆμŠ΅λ‹ˆλ‹€.</p>
  );
}
  • 각각의 μœ νš¨μ„± κ°’μœΌλ‘œ κ²€μ‚¬ν•˜κΈ° λ³΄λ‹€λŠ” λ³€μˆ˜ formIsValid κ°’ ν•˜λ‚˜λ‘œ 검사할 수 μžˆλ‹€. formIsValidλ₯Ό μ΄μš©ν•΄μ„œ λ²„νŠΌμ„ λΉ„ν™œμ„±ν™” ν–ˆκΈ° λ•Œλ¬Έμ— λ²„νŠΌμ„ λΉ„ν™œμ„±ν™”ν•œ μƒνƒœμ—μ„œλŠ” μ• μ΄ˆμ— 폼을 μ œμΆœν•  수 μ—†κΈ° λ•Œλ¬Έμ΄λ‹€.
let formIsValid = false;

if (
  enteredFirstNameIsValid &&
  enteredLastNameIsValid &&
  enteredEmailIsValid
) {
  formIsValid = true;
} else {
  formIsValid = false;
}
...

const formSubmitssionHandler = (e) => {
  e.preventDefault();

  if (
    // !enteredFirstNameIsValid ||
    // !enteredLastNameIsValid ||
    // !enteredEmailIsValid
    !formIsValid
  ) {
    return;
  }

  // first name reset
  resetFirstName();
  // last name reset
  resetLastName();
  // email reset
  resetEmail();
};
  • μ €μž₯ν•˜κ³  확인해보면, SimpleInputκ³Ό λ™μΌν•œ λ°©μ‹μœΌλ‘œ μž‘λ™ν•˜λŠ” κ±Έ μ•Œ 수 μžˆλ‹€.

ezgif com-gif-maker - 2022-06-23T184410 080


✧ useReducer μ‚¬μš©ν•˜κΈ°

  • 이전에 useReducer에 λŒ€ν•΄μ„œ ν•™μŠ΅ν–ˆμ„ λ•Œλ₯Ό κΈ°μ–΅ν•΄λ³΄μž. useReducerλŠ” μƒνƒœ(state)값듀이 많고 μ—…λ°μ΄νŠΈ 둜직이 많이 λ³΅μž‘ν•  λ•Œ μ‚¬μš©ν•œλ‹€λŠ” 것 말이닀. 그리고 κ°œλ³„μ μœΌλ‘œ κ΄€λ¦¬λ˜λ©°, μ„œλ‘œ μ—°κ΄€λ˜μ–΄μžˆλŠ” μƒνƒœ(state)에 λŒ€ν•΄μ„œλ„ useReducerλ₯Ό μ‚¬μš©ν•˜λ©΄ μ’‹λ‹€.

useInput μ»€μŠ€ν…€ ν›… λ‚΄λΆ€μ˜ input μƒνƒœ(state)λ₯Ό useReducer둜 κ΄€λ¦¬ν•˜κΈ°

  • μ§€κΈˆκΉŒμ§€ μž‘μ„±ν•œ useInput μ»€μŠ€ν…€ ν›…μ˜ λ‘œμ§λ“€μ„ 보면, μƒνƒœ(state) 값듀이 λ‹€μ–‘ν•˜μ§€λ„ μ•Šκ³ , μ—…λ°μ΄νŠΈ 둜직이 λ”±νžˆ λ³΅μž‘ν•œ 것은 μ•„λ‹ˆλ‹€. κ·Έλž˜λ„ κ·Έκ°„ κ³΅λΆ€ν•΄μ˜¨ useReducerλ₯Ό μ—°μŠ΅ν•˜κΈ° μœ„ν•΄μ„œ useInput λ‚΄λΆ€μ—μ„œ useState λŒ€μ‹  useReducerλ₯Ό ν•œ 번 μ‚¬μš©ν•΄λ³Ό 것이닀.
const [enteredValue, setEnteredValue] = useState("");
const [isTouched, setIsTouched] = useState(false);
  • μœ„μ˜ 두가지 μƒνƒœ(state)λ₯Ό useReucer둜 κ΄€λ¦¬ν•˜λ €λ©΄ μ–΄λ–»κ²Œ ν•΄μ•Ό ν• κΉŒ? λ¨Όμ € useReducerλ₯Ό import λΆ€ν„° ν•΄μ˜€μž.
import { useReducer } from "react";
  • useReducer λ₯Ό ν˜ΈμΆœν•˜κΈ° 전에 λ¨Όμ € λ¦¬λ“€μ„œ μ „μš© ν•¨μˆ˜λ₯Ό λ§Œλ“ λ‹€. 이름은 inputStateReducer 이닀.
const inputStateReducer = (state, action) => {};
  • 이전에 λ°°μ› λ‹€μ‹œν”Ό λ¦¬λ“€μ„œ ν•¨μˆ˜λŠ” 두 개의 λ§€κ°œλ³€μˆ˜λ₯Ό λ°›λŠ”λ‹€. λ¦¬μ•‘νŠΈμ—μ„œ μžλ™μœΌλ‘œ μ „λ‹¬λ˜λŠ” 이전 μƒνƒœμ— λŒ€ν•œ state 와 λ§ˆμ°¬κ°€μ§€λ‘œ λ¦¬μ•‘νŠΈμ— μ˜ν•΄ μžλ™μœΌλ‘œ μ „λ‹¬λ˜λŠ” action 이닀. 이 action은 μ½”λ“œμ— λ””μŠ€νŒ¨μΉ˜ λ˜μ–΄μ„œ μ΅œμ’…μ μœΌλ‘œλŠ” μƒˆλ‘œμš΄ μƒνƒœλ₯Ό λ°˜ν™˜ν•˜κ²Œ λœλ‹€.
const inputStateReducer = (state, action) => {
  return {
    value: "",
    isTouched: false,
  };
};

...
// const [enteredValue, setEnteredValue] = useState("");
// const [isTouched, setIsTouched] = useState(false);
  • μƒˆλ‘œμš΄ μƒνƒœλ₯Ό return ν•  λ•Œ μš°λ¦¬λŠ” μ½”λ“œλ₯Ό 바꾸지 μ•ŠλŠ” ν•œ κ·ΈλŒ€λ‘œ λ°˜ν™˜λ˜κ²Œ ν•˜λŠ” 기본적인 μƒνƒœ 값을 μ„€μ •ν•˜μ—¬ λ°˜ν™˜ν•  것이닀. μš°λ¦¬κ°€ useState둜 κ΄€λ¦¬ν•΄μ£Όμ—ˆλ˜ λ‘κ°œμ˜ μƒνƒœ(state) 값인 enteredValue와 isTouched의 초기 값을 이전과 λ™μΌν•˜κ²Œ μ„€μ •ν•΄μ€€λ‹€.
useReducer();
  • useInput λ‚΄λΆ€μ—μ„œ useReducerλ₯Ό ν˜ΈμΆœν•΄μ£Όκ³ ,
useReducer(inputStateReducer);
  • λ¦¬λ“€μ„œ ν•¨μˆ˜μΈ inputStateReducerλ₯Ό 첫 번째 인자둜 전달해쀀닀. 그리고, useInput ν•¨μˆ˜ λ°”κΉ₯에 initialInputState λΌλŠ” μƒμˆ˜λ₯Ό 생성해주고,
const initialInputState = {
  value: "",
  isTouched: false,
};
  • λ¦¬λ“€μ„œ ν•¨μˆ˜ inputStateReducer μ—μ„œ μ΅œμ’…μ μœΌλ‘œ λ°˜ν™˜ν–ˆλ˜ 초기 μƒνƒœ 객체 값을 ν• λ‹Ήν•΄μ€€λ‹€. 객체 값을 μƒμˆ˜λ‘œ ν• λ‹Ήν–ˆμœΌλ‹ˆ, ꡳ이 λ¦¬λ“€μ„œ ν•¨μˆ˜μ—μ„œ 같은 객체 값을 λ°˜ν™˜ν•  ν•„μš”λŠ” 없을 것이닀. κ·ΈλŸ¬λ‹ˆ, 객체 λŒ€μ‹  inputStateReducer μƒμˆ˜λ₯Ό λ°˜ν™˜ν•˜λ„λ‘ ν•œλ‹€.
const inputStateReducer = (state, action) => {
  return inputStateReducer;
};
  • useReducer의 두 번째 인자둜 initialInputState μƒμˆ˜λ₯Ό μΆ”κ°€ν•œλ‹€.
useReducer(inputStateReducer, initialInputState);
  • useState처럼 useReducer ν•¨μˆ˜λ„ μ •ν™•νžˆ 두 μš”μ†Œλ₯Ό 가진 λ°°μ—΄λ‘œ λ°˜ν™˜ν•  수 μžˆλ„λ‘ λ°°μ—΄ ꡬ쑰 λΆ„ν•΄ ν• λ‹ΉμœΌλ‘œ μ²˜λ¦¬ν•œλ‹€.
const [inputState, dispatch] = useReducer(inputStateReducer, initialInputState);
  • μ—¬κΈ°μ„œ 첫번째 μš”μ†ŒμΈ inputStateλŠ” λ¦¬λ“€μ„œ ν•¨μˆ˜μ— μ˜ν•΄ κ²°μ •λ˜λŠ” μƒνƒœ(state)μ—¬μ•Ό ν•˜λ©°, 두 번째 μš”μ†ŒμΈ dispatchλŠ” λ””μŠ€νŒ¨μΉ˜ ν•¨μˆ˜λ‘œ, λ¦¬λ“€μ„œ ν•¨μˆ˜μ—μ„œ μ‹€ν–‰ν•  ν•¨μˆ˜μ— λŒ€ν•œ μš”μ†Œμ΄λ‹€. 이제 값을 검증할 λ•Œ enteredValue μƒνƒœ κ°’ λŒ€μ‹ , inputState.value둜 λŒ€μ²΄ν•΄μ„œ μ‚¬μš©ν•  수 있게 λ˜μ—ˆλ‹€.

inputState μ—μ„œ value둜 μ ‘κ·Όν•  수 μžˆλŠ” μ΄μœ λŠ”, inputState의 κ΅¬μ‘°λŠ” initialInputState와 λ™μΌν•˜κΈ°μ— inputState도 λ§ˆμ°¬κ°€μ§€λ‘œ valueλΌλŠ” 속성을 κ°–κ²Œ 되기 λ•Œλ¬Έμ΄λ‹€. λ¬Όλ‘ , value처럼 isTouched도 inputState.isTouched둜 μ ‘κ·Όν•  수 μžˆλ‹€.

  • 이제 useState둜 μ‚¬μš©ν•˜λ˜ λ‘œμ§λ“€μ„ μ „λΆ€ λ³€κ²½ν•΄μ€€λ‹€.
// const valueIsValid = validateValue(enteredValue);
// const hasError = !valueIsValid && isTouched;

const valueIsValid = validateValue(inputState.value);
const hasError = !valueIsValid && inputState.isTouched;

이벀트 ν•¨μˆ˜ λ‚΄λΆ€μ—μ„œ λ””μŠ€νŒ¨μΉ˜ μ‹€ν–‰ν•˜κΈ°

const valueChangeHandler = (event) => {
  setEnteredValue(event.target.value);
};
  • valueChangeHandler 이벀트 ν•¨μˆ˜ 내뢀에 μžˆλŠ” setState μ—…λ°μ΄νŠΈ ν•¨μˆ˜λ₯Ό μ§€μš°κ³ ,
const valueChangeHandler = (event) => {
  // setEnteredValue(event.target.value);
  dispatch();
};
  • dispatch ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•΄μ„œ λ¦¬λ“€μ„œμ—κ²Œ μ‹€ν–‰ν•  μž‘μ—…μ„ λ””μŠ€νŒ¨μΉ˜ ν•˜λ„λ‘ ν•œλ‹€. 보톡은 객체 내뢀에 type μ†μ„±μœΌλ‘œ μž‘μ—…μ„ μ •ν•΄μ£ΌκΈ° λ•Œλ¬Έμ—, type 속성을 적고, 내뢀에 "INPUT" μ΄λΌλŠ” 값을 μ§€μ •ν•œλ‹€.
const valueChangeHandler = (event) => {
  dispatch({ type: "INPUT" });
};
  • 그리고 이 객체 μ•ˆμ— 두 번째 속성을 μΆ”κ°€ν•œλ‹€. value 속성을 μΆ”κ°€ν•˜κ³ , 내뢀에 event.target.valueλ₯Ό κ°’μœΌλ‘œ ν• λ‹Ήν•΄μ€€λ‹€.
const valueChangeHandler = (event) => {
  dispatch({ type: "INPUT", value: event.target.value });
};
  • 또 λ‹€λ₯Έ 이벀트 ν•¨μˆ˜λ„ μˆ˜μ •ν•΄λ³΄μž.
const inputBlurHandler = () => {
  // setIsTouched(true);
  dispatch({ type: "BLUR" });
};

const reset = () => {
  // setEnteredValue("");
  // setIsTouched(false);
  dispatch({ type: "RESET" });
};

-inputBlurHandler μ—μ„œλŠ” value 에 λŒ€ν•΄μ„œλŠ” 섀정해쀄 ν•„μš”κ°€ μ—†μœΌλ‹ˆ type : "BLUR"만 μΆ”κ°€ν•˜κ³ , reset ν•¨μˆ˜ μ—­μ‹œ type : "RESET" 이 λ‹΄κΈ΄ 객체λ₯Ό dispatch ν•¨μˆ˜μ— μ „λ‹¬ν•œλ‹€.

return {
  // value: enteredValue,
  // isValid: valueIsValid,
  value: initialInputState.value,
  isValid: valueIsValid,
  hasError: hasError,
  valueChangeHandler: valueChangeHandler,
  inputBlurHandler: inputBlurHandler,
  reset: reset,
};
  • λ§ˆμ§€λ§‰μœΌλ‘œ useInput이 λ°˜ν™˜ν•˜λŠ” κ°’ 쀑에 valueλ₯Ό enteredValueκ°€ μ•„λ‹Œ, initialInputState.value둜 μˆ˜μ •ν•΄μ€€λ‹€.

전달받은 type에 λ”°λ₯Έ μž‘μ—…μ˜ λ‚΄μš©μ„ λ¦¬λ“€μ„œ ν•¨μˆ˜μ—μ„œ μ •ν•΄μ£ΌκΈ°

  • 이제 μš°λ¦¬κ°€ dispatch둜 μ „λ‹¬ν•œ μ„Έ 가지 type("INPUT", "BLUR", "RESET")에 λŒ€ν•œ 각각의 μž‘μ—… λ‚΄μš©μ„ λ¦¬λ“€μ„œ ν•¨μˆ˜ λ‚΄λΆ€μ—μ„œ μ„€μ •ν•΄μ£Όλ©΄ λœλ‹€.
const inputStateReducer = (state, action) => {
  if (action.type === "INPUT") {
  }
  if (action.type === "BLUR") {
  }
  if (action.type === "RESET") {
  }
  return inputStateReducer;
};
  • action.type이 "INPUT" 일 λ•ŒλŠ” μ΅œμ’…μ μœΌλ‘œ μƒˆλ‘œμš΄ μƒνƒœ κ°μ²΄μ—μ„œ value의 값을 action.value둜 정해주도둝 ν•œλ‹€.
const inputStateReducer = (state, action) => {
  if (action.type === "INPUT") {
    return {};
  }
  if (action.type === "BLUR") {
  }
  if (action.type === "RESET") {
  }
  return inputStateReducer;
};
  • 그리고 μƒˆλ‘œμš΄ μƒνƒœ κ°μ²΄μ—μ„œ value의 속성과 값을 μ§€μ •ν•œλ‹€. μ΄λŠ” value λΌλŠ” 속성이 "INPUT" νƒ€μž…μΌ λ•Œ event.target.value둜 λ„˜κ²¨ 받은 값이 있기 λ•Œλ¬Έμ— κ°€λŠ₯ν•œ 것이닀. isTouchedλŠ” ν‚€λ₯Ό μž…λ ₯을 마칠 λ•Œμ—λ§Œ true 이어야 ν•˜κΈ° λ•Œλ¬Έμ—, 초기 μƒνƒœ(false)인 state.isTouched λ₯Ό κ·ΈλŒ€λ‘œ λ„˜κ²¨μ€€λ‹€.
const inputStateReducer = (state, action) => {
  if (action.type === "INPUT") {
    return { value: action.value, isTouched: state.isTouched };
  }
  if (action.type === "BLUR") {
    return {};
  }
  if (action.type === "RESET") {
  }
  return inputStateReducer;
};
  • action.type이 "BLUR" 일 κ²½μš°μ—λŠ” μ–΄λ–¨κΉŒ? μ‚¬μš©μžκ°€ input 창을 ν΄λ¦­ν•˜κ³  포컀슀λ₯Ό μžƒμ—ˆμ„ λ•Œμ—λ§Œ μž‘λ™ν•˜κ²Œ λœλ‹€. μ—­μ‹œ μƒˆλ‘œμš΄ μƒνƒœ 객체λ₯Ό λ°˜ν™˜νžˆκ³ , isTouched에 true 값을 μ „λ‹¬ν•œλ‹€. μ΄λŠ” value μ™€λŠ” 관련이 μ—†κΈ° λ•Œλ¬Έμ—, 초기 μƒνƒœμΈ state.value λ₯Ό κ·ΈλŒ€λ‘œ λ„˜κ²¨μ€€λ‹€.
const inputStateReducer = (state, action) => {
  if (action.type === "INPUT") {
    return { value: action.value, isTouched: state.isTouched };
  }
  if (action.type === "BLUR") {
    return { isTouched: true, value: state.value };
  }
  if (action.type === "RESET") {
    return {};
  }
  return inputStateReducer;
};
  • actin.type이 "RESET"일 λ•Œμ—λŠ” 말 κ·ΈλŒ€λ‘œ 값을 μ΄ˆκΈ°ν™”ν•΄μ£ΌλŠ” 퍼포먼슀λ₯Ό 보여야 ν•œλ‹€. isTouchedλ₯Ό λ‹€μ‹œ 초기 μƒνƒœ 값인 false둜 μ„€μ •ν•˜κ³ , value μ—­μ‹œ 초기 μƒνƒœμΈ 빈 λ¬Έμžμ—΄ "" 으둜 μ„€μ •ν•˜κ³  객체둜 λ‹΄μ•„μ„œ λ°˜ν™˜ν•  수 μžˆλ„λ‘ ν•΄μ€€λ‹€.
const inputStateReducer = (state, action) => {
  if (action.type === "INPUT") {
    return { value: action.value, isTouched: state.isTouched };
  }
  if (action.type === "BLUR") {
    return { isTouched: true, value: state.value };
  }
  if (action.type === "RESET") {
    return { isTouched: false, value: "" };
  }
  return inputStateReducer;
};
  • λ‹€μ‹œ μ €μž₯ν•˜κ³ , μƒˆλ‘œκ³ μΉ¨μ„ 해보면 이전과 λ™μΌν•œ λ°©μ‹μœΌλ‘œ μž‘λ™ν•˜λŠ” κ±Έ 확인할 수 μžˆλ‹€.

ezgif com-gif-maker - 2022-06-23T184410 080

정리

  • μ§€κΈˆμ²˜λŸΌ κ°„λ‹¨ν•œ ν”„λ‘œμ νŠΈμ—μ„œλŠ” ꡳ이 useReducerλ₯Ό μ‚¬μš©ν•  ν•„μš”λŠ” μ—†μ—ˆμ§€λ§Œ, κ·Έκ°„ κ³΅λΆ€ν•΄μ˜¨ 뢀뢄을 λ³΅μŠ΅ν•˜μžλŠ” μ˜λ―Έμ—μ„œ ν•΄λ‹Ή μ»€μŠ€ν…€ ν›…μ˜ μƒνƒœ 값듀에 useState λŒ€μ‹  useReducerλ₯Ό μ μš©ν•΄λ³΄μ•˜λ‹€. ν™•μ‹€νžˆ 이전보닀 이해λ₯Ό 기반으둜 ν•œ ν™œμš©μ˜ μΈ‘λ©΄μ—μ„œ μ‹€λ ₯이 ν–₯μƒλ˜μ—ˆμŒμ„ λŠλ‚„ 수 μžˆμ—ˆλ‹€. λ˜ν•œ, 이번 μ„Ήμ…˜μ„ ν†΅ν•΄μ„œ form 을 μž‘μ„±ν•˜κ³  값을 λ°›μ•„μ˜€λŠ” 것이 생각보닀 λ³΅μž‘ν•˜κ³  μ–΄λ €μš΄ μΌμž„μ„ μ•Œκ²Œ λ˜μ—ˆκ³ , 각각의 input μƒνƒœ κ°’λ§ˆλ‹€ μœ νš¨μ„± 검증을 λ”°μ Έ μ œμ–΄λ₯Ό ν•˜λŠ” 것에도 자칫 잘λͺ»ν•˜λ‹€κ°„ λ³΅μž‘ν•œ λ‘œμ§μ„ μž‘μ„±ν•  μˆ˜λ„ μžˆλ‹€λŠ” 것을 μ•Œ 수 μžˆμ—ˆλ‹€. μ§€κΈˆμ€ 비둝 κ°•μ˜μ— λ”°λ₯Έ μ—°μŠ΅μ— κ°€κΉμ§€λ§Œ 이후에 μ‹€λ¬΄μ—μ„œλ„ 배운 λ‚΄μš©μ„ 적극적으둜 μ‚¬μš©ν•  수 μžˆμœΌλ¦¬λΌλŠ” κΈ°λŒ€λ₯Ό κ°–κ²Œ λ˜μ—ˆλ‹€.

✦ 좜처


🚨 ν•΄λ‹Ή ν¬μŠ€νŒ…μ€ Udemy의 ⌜React μ™„λ²½ κ°€μ΄λ“œβŒŸ κ°•μ˜λ₯Ό 베이슀둜 ν•œ κΈ°λ‘μž…λ‹ˆλ‹€.
✍🏻 κ°•μ˜ git repo λ°”λ‘œκ°€κΈ°

profile
일단 곡뢀가 '적성'에 λ§žλŠ” 개발자. κ·Όμ„±μžˆμŠ΅λ‹ˆλ‹€.
post-custom-banner

0개의 λŒ“κΈ€