diff --git "a/CH09_\355\233\205/9.1_\353\246\254\354\225\241\355\212\270_\355\233\205/seongho.md" "b/CH09_\355\233\205/9.1_\353\246\254\354\225\241\355\212\270_\355\233\205/seongho.md" index 3beced8..b7b5a90 100644 --- "a/CH09_\355\233\205/9.1_\353\246\254\354\225\241\355\212\270_\355\233\205/seongho.md" +++ "b/CH09_\355\233\205/9.1_\353\246\254\354\225\241\355\212\270_\355\233\205/seongho.md" @@ -1 +1,225 @@ - +리액트에 훅이 추가 되기 이전에는 클래스 컴포넌트에서만 상태를 가질 수 있었고, `componentDidMount`, `componentWillUnmount`와 같은 컴포넌트 생명주기 함수 내부에서만 상태 업데이트 로직을 실행시킬 수 있었음. + + +이후 리액트에 훅이 도입되면서 함수 컴포넌트에서도 클래스 컴포넌트와 같이 생명주기에 맞춰서 로직을 실행할 수 있게 되었음 + +
+ +### useState +--- +리액트 함수 컴포넌트에서 상태를 관리하기 위해서 사용되는 대표적인 훅 +```ts +function useState( + initialState: S | (() => S) +): [S, Dispatch>]; // 함수 오버로드의 타입 선언부 + +type Dispatch = (value: A) => void; +type SetStateAction = S | ((prevState: S) => S); +``` + +> [!NOTE] +> 함수 오버로드
+> 동일한 이름에 매개 변수만 다른 여러 버전의 함수를 만드는 것을 함수의 오버로딩이라고 함
+> 파라미터의 형태가 다양한 여러 케이스에 대응하는 같은 이름을 가진 함수를 만드는 것
+> ```ts +> function makeDate(timestamp: number): Date; +> function makeDate(m: number, d: number, y: number): Date; +> function makeDate(mOrTimestamp: number, d?: number, y?: number): Date { +> if (d !== undefined && y !== undefined) { +> return new Date(y, mOrTimestamp, d); +> } else { +> return new Date(mOrTimestamp); +> } +> } +> const d1 = makeDate(12345678); +> const d2 = makeDate(5, 5, 5); +> const d3 = makeDate(1, 3); // Error! No overload expects 2 arguments, but overloads do exist that expect either 1 or 3 arguments. +> ``` +`useState`는 튜플을 반환하는데, 첫번째 요소는 제네릭 타입으로 지정한 `S`타입이고, 두번째 요소는 상태를 업데이트 할 수 있는 `Dispatch`타입의 함수임. +`useState`에 타입을 명시해주면 휴먼 에러를 방지할 수 있음 +```js +const [person, setPerson] = useState([ + { + name: 'seongho', + age: 1 + }, + { + name: 'seulgi', + age: 2 + } +]); + +setPerson([...person, { name: 'foo' , agee: 3}]); // agee + +person.reduce((acc, cur) => acc + cur.age, 0); // NaN +``` +해당 예시에 `useState`타입을 추가한다면? +```ts +interface Person { + name: string; + age: number; +} + +const [person, setPerson] = useState([ + { + name: 'seongho', + age: 1 + }, + { + name: 'seulgi', + age: 2 + } +]); + +setPerson([...person, { name: 'foo' , agee: 3}]); // 🚫 Error! + +person.reduce((acc, cur) => acc + cur.age, 0); +``` +이렇게 에러를 컴파일 타임에 발견할 수 있음 + +
+ +### 의존성 배열을 사용하는 훅 +--- +- ### `useEffect`와 `useLayoutEffect` + 리액트에서 렌더링 이후에 추가적인 사용자의 인터랙션 없이 수행해야 하는 로직이 있다면 `useEffect`를 사용할 수 있음 + ```ts + function useEffect(effect: EffectCallback, deps?: DependencyList): void; + + type DependencyList: ReadonlyArray; + type EffectCallback = () => void | Destructor; + ``` + + `useEffect`의 콜백함수로는 비동기 함수가 들어갈 수 없음. 만약 비동기함수가 들어가게 되면 `경쟁 상태`를 불러일으킬 수 있기 때문임. + +
+ + > [!NOTE] + > **경쟁 상태**
+ > 서로 다른 두 요청이 서로 경쟁하여 도착 순서를 예상할수 없게되어, 프로그램 동작이 원하지 않는 방향으로 진행되는 상태 + + `EffectCallback`타입은 아무것도 반환하지 않거나, `Destructor`를 반환하는데 이때 `Destructor`란 클린업 함수임.
+ **클린업 함수**는 해당 컴포넌트가 언마운트 될 때, 그리고 해당 `EffectCallback`이 재실행 되기 전에 실행 됨. + +
+ + useEffect와 비슷한 역할을 하는 훅으로 `useLayoutEffect`가 있음.
+ 해당 훅도 useEffect와 타입은 동일한데, 동작하는 방식에만 차이가 있음. + + ```ts + function useLayoutEffect(effect: EffectCallback, deps?: DependencyList): void; + + type DependencyList: ReadonlyArray; + type EffectCallback = () => void | Destructor; + ``` + 첫 렌더링 이후에 `EffectCallback`을 실행시키는 `useEffect`와는 다르게, `useLayoutEffect`는 첫 렌더링 전에 `EffectCallback`를 실행시킴. + +
+ +- ### `useMemo`와 `useCallback` + ```ts + type DependencyList: ReadonlyArray; + + function useMemo( + factory: () => T, + deps: DependencyList | undefined + ): T + + function useCallback any>( + callback: T, + deps: DependenctList + ): T + ``` + + > [!TIP] + > **모든 곳에 useMemo를 추가해야 할까?**
+ > `useMemo`로 최적화하는 것은 몇몇 경우에만 유용합니다.
+ > - ***useMemo에 입력하는 계산이 눈에 띄게 느리고 종속성이 거의 변경되지 않는 경우.***
+ > - ***memo로 감싸진 컴포넌트에 prop로 전달할 경우.***
+ > 값이 변경되지 않았다면 렌더링을 건너뛰고 싶을 것입니다. 메모이제이션을 사용하면 의존성이 동일하지 않은 경우에만 컴포넌트를 다시 렌더링할 수 있습니다.
+ > - ***전달한 값을 나중에 일부 Hook의 종속성으로 이용할 경우***
+ > 예를 들어, 다른 useMemo의 계산 값이 여기에 종속되어 있을 수 있습니다. 또는 useEffect의 값에 종속되어 있을 수 있습니다. + +
+ +- ### `useRef` + 리액트로 애플리케이션을 개발하다보면 DOM을 직접 선택해야 하는 경우가 발생하기 마련임 (특정 요소 위치로 스크롤하기 등)
+ 이때 `useRef`훅을 사용할 수 있음. + + ```tsx + const name = useRef(null); // ✅ OK + ``` + 근데 한가지 궁금한 점이 있다.
+ 위 코드에서 나는 `useRef`의 타입을 `string`으로 명시해줬는데, 어떻게 `null`이 초깃값으로 들어갈 수 있을까? + + 그 이유는 `useRef`가 `함수 오버로드`를 하기 때문임. + + ```ts + interface RefObject { + readonly current: T; + } + + interface MutableRefObject { + current: T; + } + + // 함수 오버로드 + function useRef(initialValue: T): MutableRefObject; + function useRef(initialValue: T | null): RefObject; + function useRef(initialValue: T | undefined): MutableRefObject; + ``` + + 만약 내가 `useRef`를 + ```tsx + const name = useRef(null); + // 또는 + const name = useRef(''); + ``` + 이렇게 작성했다면 a는 `MutableRefObject`이 되어서 `current`값을 변경할 수 있는 사이드 이펙트의 여지가 생김. + +
+ + 8장에서 보았던 것 처럼 만약 자식 컴포넌트에게 `ref`를 전달하고 싶다면 `forwardRef`를 사용해야 함 + + ```tsx + interface Props { + profileImageUrl: string; + } + + const Profile = forwardRef( + (props, ref) => { + const { profileImageUrl } = props; + + return ( + + ); + } + ); + + export default Profile + ``` +
+ + 이때 사용하는 `forwardRef`의 타입은 아래와 같음 + + ```ts + function forwardRef( + render: ForwardRefRenderFunction + ): ForwardRefExoticComponent & RefAttributes>; + + // forwardRef의 render함수타입 + interface ForwardRefRenderFunction { + (props: P, ref: ForwardedRef): ReactElement | null; + displayName?: string; + defaultProps?: never | undefined; + propTypes?: never | undefined; + } + ``` + + > [!TIP] + > `useRef`는 DOM을 직접 접근하는 용도 외에도 다양하게 활용할 수 있음
+ > - ***`useRef`로 관리되는 변수는 값이 바뀌어도 리렌더링을 촉발하지 않음.***
+ > - ***리액트의 상태는 값을 업데이트하면 리렌더링 이후에 업데이트 된 상태를 조회할 수 있지만, `useRef`는 업데이트 이후 즉시 조회가 가능함***
diff --git "a/CH09_\355\233\205/9.2_\354\273\244\354\212\244\355\205\200_\355\233\205/seongho.md" "b/CH09_\355\233\205/9.2_\354\273\244\354\212\244\355\205\200_\355\233\205/seongho.md" deleted file mode 100644 index 3beced8..0000000 --- "a/CH09_\355\233\205/9.2_\354\273\244\354\212\244\355\205\200_\355\233\205/seongho.md" +++ /dev/null @@ -1 +0,0 @@ - diff --git "a/CH09_\355\233\205/9.2_\354\273\244\354\212\244\355\205\200_\355\233\205/seulgi.md" "b/CH09_\355\233\205/9.2_\354\273\244\354\212\244\355\205\200_\355\233\205/seulgi.md" deleted file mode 100644 index 3beced8..0000000 --- "a/CH09_\355\233\205/9.2_\354\273\244\354\212\244\355\205\200_\355\233\205/seulgi.md" +++ /dev/null @@ -1 +0,0 @@ -