diff --git a/Chapter_3/Jae0/useCallback.md b/Chapter_3/Jae0/useCallback.md new file mode 100644 index 0000000..37c80b9 --- /dev/null +++ b/Chapter_3/Jae0/useCallback.md @@ -0,0 +1,21 @@ +## useCallback + +`useMemo` 에서는 전달 받은 `Callback` 의 `return` 값을 메모이제이션 한것과 유사하게 + +💡 `useCallback` 은 전달받은 `Callback` 함수 자체를 저장하여 함수를 메모이제이션 함 +⇒ 즉 특정 함수를 새로 만들지 않고 다시 재사용한다는 의미를 가짐 + +useMemo와의 차이점은 저장하고자 하는 값이 변수냐 함수냐의 차이 일뿐 + +`const memoFuncName = useCallback( Callback , [ ] )` + +- **첫 번째 인자** + + 변수는 인자로 전달받은 `Function`을 메모이제이션해 값을 가지고 있게 됨 + +- **두 번째 인자** + Array로 Array 안의 내부의 값이 변경되지 않는 이상 변경되지 않음! + +**주의점** + +🔥 함수 내부의 전역 변수를 받아 사용하는 로직이 존재한다면 해당 변수를 디펜던시 안에 넣어줘야함! diff --git a/Chapter_3/Jae0/useContext.md b/Chapter_3/Jae0/useContext.md new file mode 100644 index 0000000..3bd820e --- /dev/null +++ b/Chapter_3/Jae0/useContext.md @@ -0,0 +1,105 @@ +> **Prop Drilling & Context** +> +> 만약 최 상위 Component 가 지니고 있는 상태를 깊이 존재하는 하위 Component에게 전달하기 +> +> 위해서는 중간에 존재하는 Component들의 prop을 통해 전달 > 전달 > 전달 > 전달을 통해 +> +> 주고 받아야하는 복잡한 문제가 발생! - 문제는 하위에서 최상위로 전달할 때도 동일한 문제가 발생함 +> +> 이런 문제는 깊어지고 상태가 많아질수록 데이터 관리에 불편하고, +> +> 중간에 state가 의도와 다르게 변경될 수 있는 위험성을 초래함 +> +> 이런 과정을 PropDrilling이라고함 +> +> 그리고 이런 prop drilling을 극복하기 위해 등장한 개념이 Context 임 + +💡 이런 Prop Drilling을 개선하기 위해 Redux Recoil 등의 라이브러리가 존재하고, + +Context API는 React 라이브러리에서 기본으로 제공되는 상태 주입 API로 +상위 컴포넌트에서 만들어진 Context를 통해 하위 컴포넌트가 상태를 쉽게 사용가능하게 함 + +- 상태를 주입하는 역할을 할뿐 다른 라이브러리처럼 상태 관리를 해주는 API가 아님 + - 🔥 Context API는 특정 상태를 통해 다른 상태를 만들수 없으며, + 필요에 따라 이러한 상태 변화를 최적화 할 수 없기 때문에 상태관리 라이브러리가 아님 +- 최상단에 Context provider를 지정하기보다는 범위를 좁히고 좁혀 사용하는것이 좋음 + +**사용 방법** + +```jsx +// Context 생성 + +import { createContext, useState } from "react"; + +// createContext 를 통해 Context를 만들어줌 +export const Context = createContext(); + +// component를 umbrella로 감싸줄 외부 Wrap component 함수 만들기. +export function ContextProvider({ children }) { + // conText에서 사용할 기본 State 지정 + const [State, setState] = useState(false); + + // 상태를 변경할 Function + const Toggle = () => setState((state) => !state); + + return ( + + {/* 기본 상태와 상태변경을 위한 함수를 객체로 전달한다 */} + {children} + + ) +} + +// Context 위치 선언 +export default function App() { + // 앞서 만든 해당 Context component로 감싸 주기 + return ( + + + + ) +} + +// 하위 Component + +export default function ChildComponent (){ + // useContext 를 통해 Context 불러오기 + const { State, Toggle } = useContext(Context); + + return ( +
+ { state } + +
+ ) +} +``` + +**ContextAPI 오류 검증** + +- 만약 하위 컴포넌트에서 Context를 제대로 불러오지 못했을 때를 대비한 방어 코드 + +```tsx +export default function ChildComponent() { + // useContext 를 통해 Context 불러오기 + + function checkContext() { + const context = useContext(Context); + if (context === undefined) { + throw new Error("context가 불려오지 못함"); + } + return context; + } + + const { state, toggle } = checkContext(); + + return ( +
+ {state} + +
+ ); +} +``` diff --git a/Chapter_3/Jae0/useEffect.md b/Chapter_3/Jae0/useEffect.md new file mode 100644 index 0000000..83e8562 --- /dev/null +++ b/Chapter_3/Jae0/useEffect.md @@ -0,0 +1,113 @@ +💡 무엇인가 변화가 생겨날 대 변화를 감지하고 반응하는 Hook + +주로 화면에 처음 렌더링될때 , 다시 렌더링되는순간 , 사라지는순간 에 특정 작업을 처리하고 싶을때 사용함 + +```jsx +useEffect(() => { + // code +}, [value]); +``` + +**첫 번째 인자** + +Callback 함수를 전달받고, + +Callback함수 내부에 실행하고 싶은 코드를 작성하면 됨 + +**두 번째 인자** + +dependency Arr 를 전달받고 내부에는 값(value)을 전달 할 수 있음 + +- 전달받은 값(value)이 변화 되어질 때 마다 useEffect 내부의 Callback 함수가 실행되어짐 + +🔥 useEffect는 특별한 기능을 통해 값을 감시하지 않고, + +렌더링이 새롭게 될 때마다 의존성에 존재하는 값을 보며 의존성의 값이 이전 값과 다를때 + +전달받은 Callback 함수를 실행하게 된다. + +- 일부 외부 데이터와 동기화 하려는것이 아니라면 사용하지 않음 +- Component의 최상위 위치에서만 선언할 수 있음 +- strict 모드에서는 useEffect가 2번 실행 되어짐 + +### 의존성 + +`` + +의존성 배열 조차 전달하지 않는 경우 의존성을 비교할 필요 없이 렌더링 될 때마다 callback이 실행됨 + +⇒ 주로 component가 렌더링 된것을 확인하고싶을 때 사용함 + +```jsx +// 렌더링 테스트 코드 +useEffect(()=>{ + console.log("렌더링 발생" +}) +``` + +🔥 그렇다면 왜 아무것도 전달하지 않고 사용할까? + +1. useEffect는 component가 렌더링이 완료된 후 실행되어지고, 일반 함수는 렌더링 도중 실행되어짐 +2. 서버 사이드 렌더링 관점에서 useEffect는 클라이언트 사이드에서 실행되는것을 보장함 + +`[]` + +빈 배열을 전달한 경우 React에서는 비교할 의존성이 없다고 판단하고 최초 렌더링 직후에만 실행되어짐 + +### 주의, 권장 사항 + +- useEffect가 전달 받는 callback에 기명 함수를 사용하는것을 권장함 + + ⇒ 많은 useEffect들이 생겨나고 이를 구분하기위해 사용하기 좋음 + + ```jsx + useEffect(function 이름() {}, []); + ``` + +- useEffect는 가능한 작고 가볍게 만들수록 좋음 + +- dependency에는 외부에서 생성된 객체, 함수와 같은 참조값을 사용하면 안된다 + + ```jsx + function ChatRoom({ roomId }) { + const [message, setMessage] = useState(''); + + const options = { + serverUrl: serverUrl, + roomId: roomId + }; + + useEffect(() => { + const connection = createConnection(options); + connection.connect(); + return () => connection.disconnect(); + }, [options]); + // ... + ``` + + 위의 예시처럼 options 를 감시하고 있다면, + 만약 options의 데이터가 변하지 않은 상태에서 다른 이유로 rerender가 발생했을때 원하는 의도는 + useEffect가 다시 실행되지 않는 것을 의도함. + 하지만 🔥 options는 참조 데이터 이기때문에 rerender시에 값의 변화가 없어도 참조 주소가 변경됨 + 따라서 useEffect는 참조 주소가 변경된것을 변화로 감지하고 useEffect가 실행되어짐 + +### Clean Up + +💡 이전 값을 기준으로 실행되며, 이전 상태를 청소해주는 역할 + +```jsx +useEffect(() => { + element.addEventListener(); + + return () => { + element.removeEventListener(); + // clean up zone + }; +}); +``` + +clean up 작업은 useEffect 에 전달한 callback 함수의 return 함수 내부에서 실행되어짐 + +🔥 즉 useEffect 내부의 callback이 실행될 때 이전 clean up 함수가 존재한다면 + +clean up 함수를 실행 한 뒤 callback 함수를 실행함 diff --git a/Chapter_3/Jae0/useState.md b/Chapter_3/Jae0/useState.md new file mode 100644 index 0000000..456ab5e --- /dev/null +++ b/Chapter_3/Jae0/useState.md @@ -0,0 +1,48 @@ +💡 State ? 리액트에서 component 가 지니고 있는 상태를 의미함 + +```jsx +const [state, setState] = useState(initailState); +const [num, setNum] = useState(0); +``` + +내부의 state는 원하는 이름으로 변경 시켜 줄 수 있음 + +state + +현재의 상태값은 배열 첫번 째 아이템인 state 내부에 들어있음 + +setState + +상태를 변경 시켜주고싶을때 사용할 수 있는 함수 + +initailState + +해당 state의 초기값을 전달할 수 있음 + +만약 전달된 초기값이 없다면 `undefined` + +- setState 를 이용해 state를 변경시켜주면 해당 component 는 다시 한 번 렌더링이 진행됨 +- state를 사용하지 않고 내부에 변수를 통해 상태를 관리하게된다면, 매번 렌더링이 다시 이루어질때 + 해당 함수형 component는 매번 실행되어지고 내부 변수의 변화된 값은 매번 초기화됨 +- state가 자신의 state를 렌더링이 되어도 유지할 수 있는 이유는 closer + +### 게으른 초기화 + +💡 `useState`에 변수 대신 함수를 넘기는 방법을 게으른 초기화 라고 함 + +```jsx +// 일반적 사용 방법 +const [state,setState] = useState(Number.parseInt(window.localStorage ... )) +// 해당 로직은 state가 처음 실행되는 순간에도 복잡한 연산을 실행함 +// 그리고 값이 변경되어 다시 리렌더링 되는 순간에도 다시 복잡한 연산을 실행하게됨 + +// 게으른 초기화 +const [state,setState] = useState(()=>{ + return Number.parseInt(window.localStorage.getItem(key)) +}) +``` + +- 기본적으로 게으른 초기화를 사용하는 순간은 주로 무거운 연산을 포함하고 있을때 사용을 권장함 + ⇒ 렌더링이 다시 발생하면 인수로 전달한 실행 값이 다시 실행되기 때문 + ⇒ 배열에 대한 접근 `map` `filter` `find` 등 / Storage에 접근 +- 게으른 초기화 함수는 오로지 state가 처음 만들어지는 순간에만 사용됨