Framework/React ์™„๋ฒฝ ๊ฐ€์ด๋“œ

[React ์™„๋ฒฝ ๊ฐ€์ด๋“œ] section 10 : ๊ณ ๊ธ‰ ๋ฆฌ๋“€์„œ(Reducer)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ถ€์ž‘์šฉ ์ฒ˜๋ฆฌ & ์ปจํ…์ŠคํŠธ API

yuri lee 2022. 10. 14. 08:43
๋ฐ˜์‘ํ˜•
์ด ๊ธ€์€ udemy์˜ React ์™„๋ฒฝ ๊ฐ€์ด๋“œ with Redux, Next.js, TypeScript ๊ฐ•์ขŒ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์ •๋ฆฌํ•œ ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค.

117. ๋ชจ๋“ˆ ์†Œ๊ฐœ

  • Effects, ReducersContext
  • Working with (Side) Effects
  • MAnaging more Complex State with Reducers
  • Managing  App - Wide or Component - Wide State with Context

 

118. "Side Effects"์ด๋ž€ ๋ฌด์—‡์ด๋ฉฐ useEffect๋ฅผ ์†Œ๊ฐœํ•ฉ๋‹ˆ๋‹ค. 

  • Main Job : Render UI & React to User Input
    • Evaluate & Render JSX Manage State & Props React to (User) Events & Input Re-evaluate Component upon State & Prop Changes
    • This all is "backed into" React via the "tools" and features covered in in this course (i.e. useState() Hook, Props ect).
  • Side Effects: Anything Else
    • Store Data in Browser Storage Send Http Requests to Backend Servers Set & MAnage Times
    • These tasks must happen outside of the normal component evaluation and render cycle - especially since they might block/ delay rendering (e.g. Http requests)

Handling Side Effects with the useEffect() Hook

useEffect(() => { ... }, [ dependencies ]};
  • first parameter : A function that should be executed AFTER every component evaluation IF the specified dependencies changed. Your side effect code goes into this function.
  • second parameter : Dependencies of this effect-the function only runs if the dependencies changed. Specify your dependencies of your function here. 

 

119. useEffect() ํ›… ์‚ฌ์šฉํ•˜๊ธฐ

  • local storage ํ™œ์šฉ
  • useEffect๋Š” side effects ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ์กด์žฌํ•œ๋‹ค. side effects๋Š” ๋ณดํ†ต http request ๋“ฑ์ด๋‹ค. ์ด๋ฉ”์ผ ๋˜๋Š” ๋น„๋ฐ€๋ฒˆํ˜ธ ํ•„๋“œ์˜ ํ‚ค ์ž…๋ ฅ์— ๋Œ€ํ•œ ์‘๋‹ต์œผ๋กœ ํ•ด๋‹น form์˜ ์œ ํšจ์„ฑ์„ ํ™•์ธํ•˜๊ณ  ์—…๋ฐ์ดํŠธ ํ•˜๋Š” ๊ฒƒ ๋˜ํ•œ side effect ๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ๋‹ค. (์ด๋Š” ์‚ฌ์šฉ์ž ์ž…๋ ฅ ๋ฐ์ดํ„ฐ์˜ side effect๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ์Œ)
  • ๋ฌด์–ธ๊ฐ€์— ๋Œ€ํ•œ ์‘๋‹ต์œผ๋กœ ์‹คํ–‰๋˜๋Š” ์ฝ”๋“œ๋ฅผ ๋‹ค๋ฃจ๋Š” ๋ฐ ์ค‘์š”ํ•˜๋‹ค. ๋ฌด์–ธ๊ฐ€๋Š” ๋กœ๋“œ๋˜๋Š” ์ปดํฌ๋„ŒํŠธ์ผ ์ˆ˜๋„ ์žˆ๊ณ , ์—…๋ฐ์ดํŠธ ๋˜๋Š” ์ด๋ฉ”์ผ ์ฃผ์†Œ์ผ ์ˆ˜๋„ ์žˆ๋‹ค. ์–ด๋–ค ์•ก์…˜์— ๋Œ€ํ•œ ์‘๋‹ต์œผ๋กœ ์‹คํ–‰๋˜๋Š” ์•ก์…˜์ด ์žˆ๋‹ค๋ฉด side effects ์ด๋‹ค. 
  useEffect(() => {
    const storedUserLoggedInInformation = localStorage.getItem("isLoggedIn");
    if (storedUserLoggedInInformation === "1") {
      setIsLoggedIn(true);
    }
  }, []);

  const loginHandler = (email, password) => {
    localStorage.setItem("isLoggedIn", "1");
    setIsLoggedIn(true);
  };

  const logoutHandler = () => {
    localStorage.removeItem("isLoggedIn");
    setIsLoggedIn(false);
  };

 

121. ์ข…์†์„ฑ์œผ๋กœ ์ถ”๊ฐ€ํ•  ํ•ญ๋ชฉ ๋ฐ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š์„ ํ•ญ๋ชฉ

  • ์ƒํƒœ ์—…๋ฐ์ดํŠธ ๊ธฐ๋Šฅ
  • "๋‚ด์žฅ" API or function
  • ๋ณ€์ˆ˜๋‚˜ ํ•จ์ˆ˜

 

122. useEffect์—์„œ Cleanup ํ•จ์ˆ˜ ์‚ฌ์šฉํ•˜๊ธฐ

  • ๋””๋ฐ”์šด์‹ฑ, ์‚ฌ์šฉ์ž ์ž…๋ ฅ์„ ๋””๋ฐ”์€์ˆ˜(๊ทธ๋ฃนํ™”) ํ•˜๊ธฐ
  • (return) ์ต๋ช… ํ™”์‚ดํ‘œ ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š”๋ฐ, ์ด๋ฅผ ํด๋ฆฐ์—… ํ•จ์ˆ˜๋ผ๊ณ  ํ•œ๋‹ค. useEffect๊ฐ€ ๋‹ค์Œ ๋ฒˆ์— ์ด function์„ ์‹คํ–‰ํ•˜๊ธฐ ์ „์— clean up ํ”„๋กœ์„ธ์Šค๋กœ์จ ์‹คํ–‰๋œ๋‹ค. ์ •ํ™•ํžˆ ๋งํ•˜์ž๋ฉด useEffect ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋˜๊ธฐ ์ „ (์ฒ˜์Œ ์‹คํ–‰๋˜๋Š” ๊ฒฝ์šฐ๋ฅผ ์ œ์™ธํ•˜๊ณ ) ์ด clean up ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋œ๋‹ค.  ๋˜ํ•œ clean up ํ•จ์ˆ˜๋Š” effect๋ฅผ ํŠน์ •ํ•œ ์ปดํฌ๋„ŒํŠธ๊ฐ€ DOM์—์„œ mount ํ•ด์ œ ๋  ๋•Œ๋งˆ๋‹ค ์‹คํ–‰๋œ๋‹ค. (์ปดํฌ๋„ŒํŠธ๊ฐ€ ์žฌ์‚ฌ์šฉ๋  ๋•Œ๋งˆ๋‹ค)
  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]);

→ ์ฒ˜์Œ ๋กœ๋“œ ๋˜๋ฉด "Checking form validity" ์ด ํ‘œ์‹œ๋œ๋‹ค. ์—ฌ๊ธฐ์„œ ๋ญ”๊ฐ€๋ฅผ ํƒ€์ดํ•‘ํ•˜๋ฉด, "cleanup" ์ด ๋˜๊ณ , ๋‹ค์‹œ "Checking form validity" ๊ฐ€ ํ‘œ์‹œ๋œ๋‹ค. ์—ฌ๊ธฐ์— ๋ญ”๊ฐ€๋ฅผ ๋” ๋นจ๋ฆฌ ํƒ€์ดํ•‘ ํ•˜๋ฉด "cleanup"์ด ๋งŽ์ด ๋ณด์ธ๋‹ค. ํ•˜์ง€๋งŒ "Checking form validity" ์€ ํ•œ๋ฒˆ๋งŒ ํ‘œ์‹œ๋œ๋‹ค. ์ฆ‰, ๋ชจ๋“  ํ‚ค ์ž…๋ ฅ์— ๋Œ€ํ•ด์„œ ์ด ์ฝ”๋“œ๋Š” ํ•œ๋ฒˆ๋งŒ ์‹คํ–‰๋˜์—ˆ๋‹ค๋Š” ๋œป์ด๋‹ค. 

๋งŒ์•ฝ http request๋ฅผ ๋ณด๋‚ด๋Š” ์˜ˆ์ œ์˜€๋‹ค๋ฉด, ์ˆ˜์‹ญ๊ฐœ๊ฐ€ ์•„๋‹Œ ํ•œ๋ฒˆ๋งŒ ๋ณด๋ƒˆ์„ ๊ฒƒ. 

 

123. useEffect ์š”์•ฝ

  • useEffect besides useState is the most important React hook you have.

Basic

  useEffect(() => {
    console.log("EFFECT RUNNING");
  });

 effec function runs after every component render cycle. Not before it and not during it, but after it. 

array []

  useEffect(() => {
    console.log("EFFECT RUNNING");
  }, []);

This function only executes for the first time this component was mounted and rendered, but not therafter, not for any subsequent rerender cycle

Use dependency

  useEffect(() => {
    console.log("EFFECT RUNNING");
  }, [enteredPassword]);

→ ์ฒ˜์Œ์œผ๋กœ mount ๋˜์—ˆ์„ ๋•Œ "EFFECT RUNNING" ์ด ์‹คํ–‰๋œ๋‹ค. ์ด ๊ฒฝ์šฐ email input์— ํ‚ค๋ฅผ ์ž…๋ ฅํ•ด๋„ ์•„๋ฌด๊ฒƒ๋„ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์ง€๋งŒ, password input์— key๋ฅผ ์ž…๋ ฅํ•˜๋ฉด "EFFECT RUNNING" ๊ฐ€ ํ‘œ์‹œ๋œ๋‹ค. password๋Š” dependency์ด๊ธฐ ๋•Œ๋ฌธ! 

clean up function

  useEffect(() => {
    console.log("EFFECT RUNNING");
    return () => {
      console.log("EFFECT CLEANUP");
    };
  }, [enteredPassword]);

 

124. useReducers ๋ฐ Reducers ์ผ๋ฐ˜ ์†Œ๊ฐœ 

  • useReducer๋Š” ๋‚ด์žฅ๋œ hook์œผ๋กœ state ๊ด€๋ฆฌ๋ฅผ ๋„์™€์ค€๋‹ค. ๊ทธ๋ž˜์„œ useState์™€ ์•ฝ๊ฐ„ ์œ ์‚ฌํ•˜๋‹ค. ํ•˜์ง€๋งŒ ๋” ๋งŽ์€ ๊ธฐ๋Šฅ์„ ๊ฐ–๊ณ  ์žˆ๋‹ค. 
  • ๋” ๋ณต์žกํ•œ state๋ฅผ ๊ด€๋ฆฌํ•  ๋•Œ ์œ ์šฉํ•˜๋‹ค. (e.g. ์—ฌ๋Ÿฌ state์ด ํ•จ๊ป˜ ์†ํ•ด์žˆ๋Š” ๊ฒฝ์šฐ or ์—ฌ๋Ÿฌ state๊ฐ€ ๊ฐ™์ด ๋ฐ”๋€Œ๊ฑฐ๋‚˜ ์„œ๋กœ ๊ด€๋ จ๋œ ๊ฒฝ์šฐ์—๋Š” useState๋ฅผ ์ด์šฉํ•œ state๋Š” ์ข…์ข… ์‚ฌ์šฉ ๋ฐ ๊ด€๋ฆฌ๊ฐ€ ์–ด๋ ค์งˆ ์ˆ˜ ์žˆ์Œ)
  • useReducer๋Š” useState๋ณด๋‹ค ์‚ฌ์šฉํ•˜๊ธฐ๊ฐ€ ์กฐ๊ธˆ ๋” ๋ณต์žกํ•ด ์„ค์ •์ด ๋” ํ•„์š”ํ•˜๋‹ค. ๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ์—๋Š” useState๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ์ข‹์ง€๋งŒ, useReducer์˜ ์ถ”๊ฐ€ ์ž‘์—…์„ ํ• ๋งŒํ•œ ๊ฐ€์น˜๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ ์‚ฌ์šฉํ•˜๋ฉด ์ข‹๋‹ค. 

 

125. useReducer() hook ์‚ฌ์šฉ

const [state, dispatchFn] = useReducer(reducerFn, initialState, initFn);
  • state : The state snapshot used in the component re-render/ re-evaluation cycle
  • dispatchFn : A function that can be used to dispatch a new action (i.e. trigger an update of the state)
  • reducerFn : (preveState, action) => newState / A function that is triggered automatically once an action is dispatched (via dispatchFn()) - it receives the latest state snapshot and should return the new, updated state.
  • initialState. : The initial state
  • initFn : A function to set the initial state programmatically

emailState๋ฅผ useReducer๋กœ ๊ด€๋ฆฌํ•ด๋ณด๋„๋ก ํ•˜์ž. 

 

 const [emailState, dispatchEmail] = useReducer(emailReducer, {
    value: "",
    isValid: false,
  });
const emailReducer = (state, action) => {
  if (action.type === "USER_INPUT") {
    return { value: action.val, isValid: action.val.includes("@") };
  }

  if (action.type === "INPUT_BLUR") {
    return { value: state.value, isValid: state.value.includes("@") };
  }
  return { value: "", isValid: false };
};

reducer function์„ ์ปดํฌ๋„ŒํŠธ ํ•จ์ˆ˜ ๋ฐ”๊นฅ์— ๋งŒ๋“ค์—ˆ๋‹ค๋Š” ๊ฒƒ์— ์ฃผ๋ชฉํ•˜์ž. ๋ฆฌ๋“€์„œ ํ•จ์ˆ˜ ๋‚ด๋ถ€์—์„œ๋Š” ์ปดํฌ๋„ŒํŠธ ํ•จ์ˆ˜ ๋‚ด๋ถ€์—์„œ ๋งŒ๋“ค์–ด์ง„ ์–ด๋–ค ๋ฐ์ดํ„ฐ๋„ ํ•„์š”ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๋”ฐ๋ผ์„œ ์ปดํฌ๋„ŒํŠธ ํ•จ์ˆ˜์˜ ๋ฒ”์œ„ ๋ฐ–์—์„œ ๋งŒ๋“ค์–ด์งˆ ์ˆ˜ ์žˆ๋‹ค.

 

126.  userReducer & useEffect

passwordState๋ฅผ useReducer๋กœ ๊ด€๋ฆฌํ•ด๋ณด๋„๋ก ํ•˜์ž. 

ํ•˜์ง€๋งŒ ์ด๋Š” ์ตœ์ ์˜ ์ฝ”๋“œ๊ฐ€ ์•„๋‹ˆ๋‹ค. 

 

  const [formIsValid, setFormIsValid] = useState(false);

์—ฌ๊ธฐ์„œ ๋” ์ค‘์š”ํ•œ ๊ฒƒ์€ ํผ ์œ ํšจ์„ฑ์ด๋‹ค. ์ž…๋ ฅ์€ ์ „์ฒด ํผ์˜ ์ผ๋ถ€์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

 

ex) 
const emailChangeHandler = (event) => {
    dispatchEmail({ type: "USER_INPUT", val: event.target.value });
    setFormIsValid(event.target.value.includes("@") && passowrdState.isValid);
};

์—ฌ์ „ํžˆ ํผ ์œ ํšจ์„ฑ์„ ๋‹ค๋ฅธ state์—์„œ ๋„์ถœํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ด๋Š” ๋ฆฌ์•กํŠธ์˜ state ์—…๋ฐ์ดํŠธ Scheduling ๋•Œ๋ฌธ์ด๋‹ค. 

 

๋ฆฌ์•กํŠธ์˜ state ์—…๋ฐ์ดํŠธ Scheduling

๋ณ€ํ•˜์ง€ ์•Š์€ ์ด์ „์˜ state๋ฅผ ์ฐธ์กฐํ•˜๊ฒŒ ๋  ์ˆ˜๋„ ์žˆ๋‹ค. state๊ฐ€ useReducer์˜ setFormIsValid์—์„œ ์˜ค๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๋”ฐ๋ผ์„œ ์ด๋Š” ์ตœ์ ์˜ ์ฝ”๋“œ๋Š” ์•„๋‹ˆ๋‹ค. ๋˜ํ•œ ๊ธฐ์ˆ ์ ์œผ๋กœ ํ•จ๊ป˜ ์†ํ•˜๋Š” state๋“ค์ด ๋ถ„ํ™œ๋œ ๊ฒƒ์„ ์›ํ•˜์ง€ ์•Š์„ ๊ฒƒ์ด๋‹ค. ๋‹ค์‹œ ์ฃผ์„ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๊ณ ,  useEffect๋ฅผ ์‚ฌ์šฉํ•ด๋ณด๋„๋ก ํ•˜์ž! 

 

UseEffect ํ•ด๊ฒฐ๋ฒ•

  const emailChangeHandler = (event) => {
    dispatchEmail({ type: "USER_INPUT", val: event.target.value });
    // setFormIsValid(event.target.value.includes("@") && passowrdState.isValid);
  };
  useEffect(() => {
    const identifier = setTimeout(() => {
      console.log("Checking form validity");
      setFormIsValid(
        emailState.isValid && passowrdState.isValid
      );
    }, 500);
    return () => {
      console.log("CLEANUP");
      clearTimeout(identifier);
    };
  }, [emailState, passowrdState]);

useEffect ์•ˆ์— ์žˆ๊ธฐ ๋•Œ๋ฌธ์— state๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ํ™•์‹คํžˆ ๋‹ค์‹œ ์‹คํ–‰๋œ๋‹ค. ์ตœ์‹  state ๊ฐ’์œผ๋กœ!

 

ํ•œ๊ฐ€์ง€ ๋” ๋ฌธ์ œ !

effect๊ฐ€ ๋„ˆ๋ฌด ์ž์ฃผ ์‹คํ–‰๋˜๊ณ  ์žˆ๋‹ค. ์ด ์ฝ”๋“œ๋Š” emailState ๋‚˜ passwordState๊ฐ€ ๋ณ€ํ• ๋•Œ๋งˆ๋‹ค ์‹คํ–‰๋œ๋‹ค.  ๊ฐ’๋งŒ ๋ณ€๊ฒฝ๋œ ๊ฒฝ์šฐ๋„ ์žˆ์„ ์ˆ˜๊ฐ€ ์žˆ๋‹ค. ์ด๋ฏธ input์ด ์œ ํšจํ•œ๋ฐ, ๋น„๋ฐ€๋ฒˆํ˜ธ์— ๋ฌธ์ž๋ฅผ ์ถ”๊ฐ€ํ•œ ๊ฒฝ์šฐ๋ผ๋ฉด ๋ฐ”๋€Œ์ง€ ์•Š๋Š”๋‹ค. ๋ฌธ์ž๋ฅผ ์ถ”๊ฐ€ํ•˜๊ธฐ ์ „์—๋„ ์œ ํšจํ–ˆ๊ณ , ์ดํ›„์—๋„ ์—ฌ์ „ํžˆ ์œ ํšจํ•˜๋‹ค. 

 

object de-structuring

  const { isValid: emailIsValid } = emailState;
  const { isValid: passwordIsValid } = passowordState;

  useEffect(() => {
    const identifier = setTimeout(() => {
      console.log("Checking form validity");
      setFormIsValid(emailIsValid && passwordIsValid);
    }, 500);
    return () => {
      console.log("CLEANUP");
      clearTimeout(identifier);
    };
  }, [emailIsValid, passwordIsValid]);

 alias assignment ์‚ฌ์šฉํ•˜๊ธฐ / de-structuring ๊ตฌ๋ฌธ ์‚ดํŽด๋ณด๊ธฐ

→ ๊น‚์„ ํ• ๋‹นํ•˜๋Š” ๊ฒƒ์€ ์•„๋‹˜, ๋“ฑํ˜ธ์˜ ์™ผ์ชฝ์—์„œ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ / ๊ฐ์ฒด์—์„œ๋Š” ํ•ด๋‹น ๊ฐ’์„ ์ถ”์ถœํ•˜๊ธฐ ์œ„ํ•ด alias๋ฅผ ํ• ๋‹นํ•œ๋‹ค. (์†์„ฑ ์ถ”์ถœ์„ ์œ„ํ•ด)

isValid ์†์„ฑ์„ ๊บผ๋‚ด์–ด ์ƒ์ˆ˜์— ์ €์žฅํ•˜๊ณ , ๊ฐ๊ฐ ์ด๋ฆ„์„ ๋ถ™์—ฌ๋ณด๋„๋ก ํ•˜์ž. ์ด๋ฅผ alias assignment ๋ผ๊ณ  ํ•œ๋‹ค.  

๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ 6๊ธ€์ž ์ด์ƒ์ผ ๊ฒฝ์šฐ ๋”์ด์ƒ useEffect๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค. 

 

127. ์ค‘์ฒฉ ์†์„ฑ์„ useEffect์— ์ข…์†์„ฑ์œผ๋กœ ์ถ”๊ฐ€ํ•˜๊ธฐ 

const { someProperty } = someObject;
useEffect(() => {
  // code that only uses someProperty ...
}, [someProperty]);

useEffect()์— ๊ฐ์ฒด ์†์„ฑ์„ dependencies์œผ๋กœ ์ถ”๊ฐ€ํ•˜๊ธฐ ์œ„ํ•ด destructuring์„ ์‚ฌ์šฉํ–ˆ๋‹ค. ์ด๋Š” ๋งค์šฐ general ํ•œ ํŒจํ„ด ์ ‘๊ทผ ๋ฐฉ์‹์ด๋‹ค. main point๋Š” destructuring์„ ์‚ฌ์šฉํ•œ๋‹ค๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ์ „์ฒด ๊ฐœ์ฒด ๋Œ€์‹  ํŠน์ • ์†์„ฑ์„ dependencies ์œผ๋กœ ์ „๋‹ฌํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. 

useEffect(() => {
  // code that only uses someProperty ...
}, [someObject.someProperty]);

๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์—ฌ๋„ ์ž‘๋™ํ•˜์ง€๋งŒ, ์œ„์™€ ๊ฐ™์€ ํŒจํ„ด์€ ํ”ผํ•ด์•ผ ํ•œ๋‹ค. effect ํ•จ์ˆ˜๋Š” someObject ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ์žฌ์‹คํ–‰๋˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. 

 

128. State ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•ธ useReducer vs useState

  • Generally, you'll know when you need useReducer() : when using useState() becomes cumbersome or you're getting a lot of bugs / unintended behaviors)

useState()

  • The main state management "tool"
  • Great for independent pieces of state/data
  • Greate if state updates are easy and limited to a few kinds of updates

useReducer()

  • Greate if you need "more power"
  • Should be considered if you have related pieces of state/data
  • Can be helpful if you have more complex state updates

129. ๋ฆฌ์•กํŠธ Context(Context API) ์†Œ๊ฐœ 

๋กœ๊ทธ์ธ state๋Š” ์•ฑ์˜ ๋‹ค์–‘ํ•œ ์œ„์น˜์—์„œ ํ•„์š”ํ•˜๊ฑฐ๋‚˜ ์‚ฌ์šฉ๋œ๋‹ค. ์ด๊ฒƒ์€ ์•„์ฃผ ๊ฐ„๋‹จํ•œ ์•ฑ์ด๋‹ค. ์—ฌ๊ธฐ์„œ state๋ฅผ ์ด๋ ‡๊ฒŒ ์ „๋‹ฌํ•ด๋„ ํฐ ๋ฌธ์ œ๋Š” ๋˜์ง€ ์•Š๋Š”๋‹ค. 

 

๋ฌธ์ œ๊ฐ€ ๋˜๋Š” ๊ฒฝ์šฐ๋Š”, state๋ฅผ ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ†ตํ•ด ์ „๋‹ฌํ•˜๋Š” ๊ฒฝ์šฐ์ด๋‹ค. ์•ฑ์ด ์ปค์งˆ์ˆ˜๋ก ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋”์šฑ ๋ถˆํŽธํ•ด์ง„๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋Š” ๋Œ€์‹  props๋ฅผ ์‹ค์ œ๋กœ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถ€๋ชจ๋กœ๋ถ€ํ„ฐ ๋ฐ›๋Š” ์ปดํฌ๋„ŒํŠธ์—์„œ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฉด ๋” ์ข‹์„ ๊ฒƒ์ด๋‹ค. ๋ถ€๋ชจ๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•˜์ง€ ์•Š๋„๋ก! ๋ถ€๋ชจ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•˜์ง€๋„ ํ•„์š”ํ•˜์ง€๋„ ์•Š์Œ. 

 

์ปดํฌ๋„ŒํŠธ ์ „์ฒด์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ฆฌ์•กํŠธ์— ๋‚ด์žฅ๋œ/๋‚ด๋ถ€์ ์ธ state ์ €์žฅ์†Œ๊ฐ€ ์žˆ๊ณ , ์ด๋Š” react context ๋ผ๋Š” ๊ฐœ๋…์ด๋‹ค.  ์ด๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๊ธด porps ์ฒด์ธ์„ ๋งŒ๋“ค์ง€ ์•Š๊ณ ๋„, state๋ฅผ ๊ด€๋ จ๋œ ์ปดํฌ๋„ŒํŠธ์— ์ง์ ‘ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค. 

 

130. ๋ฆฌ์•กํŠธ ์ปจํ…์ŠคํŠธ API ์‚ฌ์šฉ

๋ฆฌ์•กํŠธ ์ปจํ…์ŠคํŠธ๋ž€, ๋ฆฌ์•กํŠธ ๋‚ด๋ถ€์ ์œผ๋กœ state๋ฅผ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ฃผ๋Š” ๊ฒƒ์ด๋‹ค. ์•ฑ์˜ ์–ด๋–ค ์ปดํฌ๋„ŒํŠธ์—์„œ๋ผ๋„ ์ง์ ‘ ๋ณ€๊ฒฝํ•˜์—ฌ ์•ฑ์˜ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ์— ์ง์ ‘ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค€๋‹ค. (ํ”„๋กญ์ฒด์ธ ์—†์ด๋„)

 

1. src/store ํด๋” ๋งŒ๋“ค๊ธฐ

2. auth-context.js ํŒŒ์ผ ์ถ”๊ฐ€ (์ผ€๋ฐฅ ์ผ€์ด์Šค ์‚ฌ์šฉ)

 

provider

<AuthContext.Consumer></AuthContext.Consumer>

 

listener 2 way

1. Auth-Context consumer

<AuthContext.Consumer></AuthContext.Consumer>

→ the consumer is just one way of consuming our Context. 

  It's an okay-ish why, but i don't like that syntax too much

 

 

2. React Hook

import React, { useContext } from "react";

const ctx = useContext(AuthContext);โ€‹

it is a bit more elegant and a bit nicer than using AuthContext Consumer

 

131.  useContext ํ›…์œผ๋กœ ์ปจํ…์ŠคํŠธ์— ํƒญํ•‘(tapping)ํ•˜๊ธฐ

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>
  );
};

→ This is just a bit more elegant way in my opinion (teacher's opinion)

132. ์ปจํ…์ŠคํŠธ๋ฅผ ๋™์ ์œผ๋กœ ๋งŒ๋“ค๊ธฐ 

  return (
    <AuthContext.Provider
      value={{
        isLoggedIn: isLoggedIn,
        onLogout: logoutHandler,
      }}
    >
      <MainHeader onLogout={logoutHandler} />
      <main>
        {!isLoggedIn && <Login onLogin={loginHandler} />}
        {isLoggedIn && <Home onLogout={logoutHandler} />}
      </main>
    </AuthContext.Provider>
  );

→ isLoggedIn์„ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ ์™ธ์—๋„ ํ•จ์ˆ˜๋ฅผ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค. (๋ฌธ์ž์—ด์ด๋‚˜ ๊ฐ์ฒด ๋“ฑ์˜ ๊ฐ’์€ ์ „๋‹ฌํ•  ์ˆ˜ ์—†์Œ) 

props์€ ๋”์ด์ƒ Navigation ์ปดํฌ๋„ŒํŠธ์— ํ•„์š” ์—†์œผ๋ฏ€๋กœ ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ๋‹ค. 

 

133. ์‚ฌ์šฉ์ž ์ •์˜ ์ปจํ…์ŠคํŠธ ์ œ๊ณต์ž ๊ตฌ์„ฑ ์š”์†Œ ๋นŒ๋“œ ๋ฐ ์‚ฌ์šฉ

import React from "react";

const AuthContext = React.createContext({
  isLoggedIn: false,
  onLogout: () => {},
});

export default AuthContext;

IDE ์ž๋™ ์™„์„ฑ์„ ๋” ์ข‹๊ฒŒ ๋งŒ๋“ค๋ ค๋ฉด ๊ธฐ๋ณธ ์ปจํ…์ŠคํŠธ ๊ฐ์ฒด์— onLogout์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค. (๊ทธ๋ƒฅ ๋”๋ฏธ ํ•จ์ˆ˜๋กœ ๋„ฃ์–ด๋„ ๋จ)

 

๋ณ„๋„์˜ ์ปจํ…์ŠคํŠธ ๊ด€๋ฆฌ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค๊ณ  ์‹ถ๋‹ค๋ฉด? 

# auth-context.js 

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");
    setIsLoggedIn(true);
  };

  return (
    <AuthContext.Provider
      value={{
        isLoggedIn: isLoggedIn,
        onLogout: logoutHandler,
        onLogin: loginHandler,
      }}
    >
      {props.children}
    </AuthContext.Provider>
  );
};

export default AuthContext;

# index.js

import React from "react";
import ReactDOM from "react-dom/client";

import "./index.css";
import App from "./App";
import { AuthContextProvider } from "./store/auth-context";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <AuthContextProvider>
    <App />
  </AuthContextProvider>
);

App, Home, Login ์ปดํฌ๋„ŒํŠธ์—๋„ ์ถ”๊ฐ€ ์ž‘์—…์„ ํ•ด์ค€๋‹ค. 

const ctx = useContext(AuthContext);

 

์žฅ์ 

์ด๋ ‡๊ฒŒ ํ•˜๋Š” ๊ฒƒ์˜ ์žฅ์ ์€ ์•ฑ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋” ๊ฐ„๊ฒฐํ•ด์ง„๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. state, authContext ๊ด€๋ฆฌ๋ฅผ ๋ชจ๋‘ ํ•˜๋‚˜์˜ ํŒŒ์ผ์—์„œ ํ•œ๋‹ค. 

 

134. ๋ฆฌ์•กํŠธ ์ปจํ…์ŠคํŠธ ์ œํ•œ (Context Limitations)

  • React Context is Not optimized for highj frequency changes! (We'll explore a better tool for that, later)
  • React Context also shouldn't be used to replace ALL component communications and props (Component should still be configurable via props and short "prop chains" might not need any replacement)

๋ณ€๊ฒฝ์ด ์žฆ์€ ๊ฒฝ์šฐ์—, ๋ฆฌ์•กํŠธ ์ปจํ…์ŠคํŠธ๋Š” ๊ทธ๋‹ค์ง€ ์ ํ•ฉํ•˜์ง€ ์•Š๋Š”๋‹ค. ์ตœ์ ํ™” ๋˜์–ด ์žˆ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ. (๊ณต์‹ ๋ฆฌ์•กํŠธ ํŒ€์›๊ป˜์„œ ๋ง์”€ํ•˜์‹  ๋‚ด์šฉ) 

๋” ๋‚˜์€ ๋„๊ตฌ์— ๋Œ€ํ•ด์„œ๋Š” ๋‚˜์ค‘์— ์ด์•ผ๊ธฐ ํ•  ์˜ˆ์ •. ์ฐธ๊ณ ๋กœ ๊ทธ ๋„๊ตฌ ์ด๋ฆ„์€ ๋ฆฌ๋•์Šค(Redux)

 

135. "Hooks์˜ ๊ทœ์น™" ๋ฐฐ์šฐ๊ธฐ  โœ…

  • Only call React Hooks in React Functions
    • React Component Functions
    • Custom Hooks (covered later!)
  • Only call React Hooks at the Top Level
    • Don't call them in nested functions
    • Don't call them in any block statements
  • +extra, unofficial Rule for useEffect(): Always add everything you refer to inside of useEffect() as a dependency! 

 

136. ์ž…๋ ฅ ์ปดํฌ๋„ŒํŠธ ๋ฆฌํŒฉํ† ๋ง 

Login Component ์— ์žˆ๋Š” ์ค‘๋ณต๋œ input (for id, password) ์„ ํ•˜๋‚˜์˜ ์ปดํฌ๋„ŒํŠธ๋กœ ๋ฆฌํŒฉํ† ๋ง ํ•ด๋ณด์ž! 

const Input = (props) => {
  return (
    <div
      className={`${classes.control} ${
        props.isValid === false ? classes.invalid : ""
      }`}
    >
      <label htmlFor={props.id}>{props.label}</label>
      <input
        type={props.type}
        id="email"
        value={props.value}
        onChange={props.onChange}
        onBlur={props.onBlur}
      />
    </div>
  );
};

export default Input;

 

137. Forword Refs'์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๊ธฐ โœ…

  const submitHandler = (event) => {
    event.preventDefault();
    if (formIsValid) {
      authCtx.onLogin(emailState.value, passowordState.value);
    } else if (!emailIsValid) {
      emailInputRef.current.activate();
    } else {
      passwordInputRef.current.activate();
    }
  };

 ์—๋Ÿฌ ๋ฐœ์ƒ 

React.forwardRef / useImperativeHandler

  • React.forwardRef ์™€ useImperativeHandler ๋ฅผ ํ™œ์šฉํ•˜์—ฌ ํ•ด๋‹น ์—๋Ÿฌ๋ฅผ ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ๋‹ค. 

 

138. ๋ชจ๋“ˆ ๋ฆฌ์†Œ์Šค 

  • ๊ฐ•์˜ ์†Œ์Šค์ฝ”๋“œ ์ฐธ๊ณ  
๋ฐ˜์‘ํ˜•