Skip to content

Commit

Permalink
3장 내용 업데이트
Browse files Browse the repository at this point in the history
  • Loading branch information
sensecodevalue committed Jan 30, 2024
1 parent fab0a34 commit 3c3859b
Showing 1 changed file with 78 additions and 103 deletions.
181 changes: 78 additions & 103 deletions 3장/정대윤.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,76 +18,55 @@ const global {}
let index = 0

function useState(initialState) {
// 최초 접근할 경우, [] 할당
if(!global.states) globae.states = [];

// states 정보를 조회 현재 상태값 있는지 확인
// 없으면 초기값 할당
// states의 값을 현재 값으로 업데이트
**const currentState = global.sttates[index] || initialState; //=> ?? 이지 않을까?**
global.states[index] = currentState;

// 즉시실행 setter func
const setState = (function () {
// 만들어질 당시의 index를 클로저에 가두어 둔어 동일한 index에 접근할 수 있도록한다.
// 만들어질 당시의 index 값을 클로저활용해 가두어 둔어 동일한 index에 접근할 수 있도록한다.
let currentIndex = index;
return function (value) {
global.states[currentIndex] = value;
}
})();
// useState를 쓸 때마다 index를 하나씩추가한다.
// 어차피 트리구조로 순회하면서 useState가 만들어질태니 계속 같은 값을 할당받을 수 있는 구조인듯?
index = index + 1;

index = index + 1;
return [currentState, setState];
}

```

와 클로저에 의존해 구현되어 있다 실제로는 useReducer를 통해 구현되어 있다고 한다.
실제로는 useReducer를 통해 구현되어 있다고 한다.

### 게으른 초기화?

> 처음 들어보고 생각조차 안하고 있던 것….
useState 초기값이 복잡하거나 무거운 연산을 포함하고 있을 경우 사용된다.
> useState 초기값이 복잡하거나 무거운 연산을 포함하고 있을 경우 사용됨.
`const [] = useState(window.localStorage.geItem('key'))`
`const [] = useState( () => window.localStorage.geItem('key'))`

함수형 컴포넌트에서는 렌더링마다 함수가 실행되며, useState도 재실행된다.

하지만 앞서 코드로 inital은 최초이후에는 발생하지 않는다.

따라서 inital에 넣으면 이후에는 발생하지 않아 계속 함수가 실행되는것을 방지할 수 있다.

**무거운 작업일 경우에!! 를 집중하자, 코드의 가독성을 위해서 변수로 함수로 따로 존재할 수도 있다.**

> 그렇다면 무거운 작업은?
> window storage 접근, map, filter, find 배열 연산작업 등이 될 수 있다.
> 근데 이렇게한 경우가 많았던가를 복기해보면 별로 없었던듯하다.
> 특정 객체에 지속적인 참조를하는 경우(window storage), map, filter, find 배열 연산작업 등이 될 수 있다.
## useEffect

> Class component의 생명주기를 대체하기 위해 만들어진 훅이 아니다?!
> 컴포넌트의 값을 활용해서 동기적으로 side-effect를 만드는 메커니즘이다.
> 따라서 `언제``상태` 를 잘파악해야된다.
## useEffect는 어떻게 의존성 값의 변경을알까?
- 컴포넌트 주기와 비슷한 동작을 구현할 수 있음. (하지만 `언제`보다 `상태`에 집중한다.)
- 컴포넌트가 언마운트되었을 경우, 클린업이 실행됨
- 다음 effect-callback이 실행되기전에 이전의 클린업이 존재한다면 클린업 실행

함수형 컴포넌트는 함수를 실행 ⇒ 렌더링을 수행한다.

이때, state와 props의 변화가 있다면 그냥 실행하는 함수이다.
이때, state와 props의 (Object.is를 통한)변화가 있다면 그냥 실행하는 함수이다.

> useEffect는 proxy나 옵저버로 값 변화를 감지하는것이 아니다
### 클린업 함수

클린업 함수는 보통 이벤트를 등록 or 삭제할 때 사용한다.

클린업 함수는 새로운 값과 함께 랜더링된 뒤에 실행된다.
클린업 함수는 보통 이벤트를 등록 or 삭제할 때 사용.

**새로운 값을 기반으로 실행되지만 함수가 정의 됐을 당시에 값을 보고 실행한다.**
클린업 함수는 새로운 값과 함께 렌더링된 뒤에 실행됨. (언마운트에도 실행됨)

따라서 useEffect내에서 이벤트를 추가할 경우, 클린업 함수에서 제거한다면, 이전의 event를 제거하고 무한히 이벤트가 등록되는것을 막을 수 있는 것이다.
**새로운 값을 기반으로 실행되지만 함수가 선언 됐을 당시에 값을 보고 실행한다. (클로저)**

```jsx
const [foo] = useState(1);
Expand All @@ -106,136 +85,132 @@ useEffect(() => {
}, []);
```

이것은 생명주기 메서드의 언마운트 개념과는 차이가 있다.

> **클린업 함수는 리렌더링 이후, 의존성 변화가 있을 당시 이전 값을 기준으로 실행되는 함수이다.**
> 이것은 상태를 청소해주는 개념이다.
> **청소해준다는 개념이 무엇을 의미하는것인지 모르겠다…**
> 클린업 함수는 리렌더링 이후, 의존성 변화가 있을 당시 이전 값을 기준으로 실행되는 함수이다.
### 의존성 배열

의존성을 비어두면 최초 이후에는 리렌더링시에 의존성의 변경이 없다고 판단하고 실행하지 않는다.

BUT 아에 넘기지 않는다면 `useEffect(() => {})` 매 랜더링 마다 실행하겠다는 것을 의미한다.

함수에 in-line으로 넣는것과는 다르다.

- useEffect는 client환경임을 보장한다.
- useEffect는 렌더링된 이후에 실행됨으로 렌더링 지연시키지 않는다.

```jsx
const global = {};
let index = 0;

function useEffect(callback, dependencies) {
const hooks = global.hooks;
let perivousDependencies = hooks[index]; // 이전 훅 있냐?
함수에 in-line(body)으로 넣는것과는 다르다.

// 이전꺼 없으면 true || 바뀐거 있어도 true
let isDependenciesChanged = perivousDependencies ?
dependencies.some((v, i) => !Object.is(v, perivousDependencies[i])
: true;

if(isDependenciesChanged) callback();

hooks[index] = dependencies; // 의존성 값다시 저장 => 이렇게 매번 확인 -> 실행함

index++;
}

return { useEffect }
```
- client환경임을 보장한다.
- 렌더링된 이후에 실행됨으로 렌더링 지연 없음.
- in-line(body) 컴포넌트 반환을 느리게할 수 있기에 렌더링을 방해할 수 있음.

### useEffect 주의점

1. eslint-diable-line, react-hooks/exhaustive-deps 주의
1. 의존성 배열에 없는것을 useEffect 내부에서 사용 ⇒ useEffect는 의존성의 부수효과를 다루니 없는것 경우를 최소화해보자.
2. **useMemo | Callback을 활용한 의존성 배열을 관리해서 최초에만 적용할 수 있도록할 수 있다.**
- 아까 보았듯이 window를 사용할때 등에서는 사용해야될 듯.
- 솔직히 이걸 어캐하라는건지 모르겠음?! 해본적있는 분이 계신가요?!
2. useEffect의 callback에 함수명 넣기
1. `useEffect(function foo(){}, [])`
3. 거대한 useEffect 회피
1. 의존성 배열에 없는것 줄이기
1. 줄이려고는 노력은하겠다만..글쌔 이게 완전히 없애기에는 힘들듯 당장 window 접근, dom 접근, ref접근 할때만 봐도 그럼.
2. 거대한 useEffect 회피
1. 의존성 변경 ⇒ 렌더링 마다 실행됨으로 크기가 클 수록 악영향을 미친다.
4. 불필요한 외부함수 선언
2. 작은 단위로 쪼개야 코드의 유지보수성이 오름.
3. 불필요한 외부함수 선언
1. 어차피 의존성 변경 ⇒ 렌더링마다 생성 됨으로 useCallback 등 불필요한 코드가 증가한다.

### useEffect callback async 불가

비동기 함수를 사용할 경우, state의 경쟁상태가 될 수 있기 때문에 사용하지 않도록한다.
**비동기 useEffect** 사용할 경우, state의 경쟁상태가 될 수 있기 때문에 사용하지 않도록한다.

- cleanup 함수 실행 순서도 보장되지 않는다.
- cleanup 함수 실행 순서도 보장되지 않음
- state를 비동기로 변경한다고 했을 때, 순서에 따른 변경이 아닌 예측할 수 없는 값이 보여질 수 있음.

> state를 비동기로 변경한다고 했을 때, 순서에 따른 변경이 아닌 예측할 수 없는 값이 보여질 수 있다.
즉시 실행함수나 promise chaining으로 실행을 가능하지만, state나 abortController등으로 예측이 가능할 수 있도록 비동기 실행 이후, 이전 요청을 취소해야함.

## useRef

컴포넌트에서 렌더링이 일어나도(함수가 실행되도) 변경 가능한 상태 값을 저장하는 역할이다.
컴포넌트에서 렌더링 없이 고정된 값을 관리할 수 있음.

- useRef는 current로 값 접근 변경 가능
- 값이 변해도 렌더링 안함
- 컴포넌트 생애주기에 따라 생성되고 삭제되며, 컴포넌트별로 별개로 선언 관리할 수 있음.

> 렌더링 없이 고정된 값을 관리할 수 있음
dom 접근 및 previousState를 저장할 경우에도 많이 쓰인다.
dom 접근 및 previousState를 저장할 경우에도 많이 쓰임.

### useContext

> useContext는 상태관리 x ⇒ 상태 주의 API이다.
> useContext는 상태관리 x ⇒ 상태 주입 API이다.
상태를 기반으로 상태를 못 만들며, 상태 변화를 최적화를 하지 못한다.
상태를 기반으로 상태를 못 만들며, 상태 변화를 최적화를 하지 못함.

⇒ context는 전역으로 사용하면 하위가 모두가 렌더링 되는것은 말그대로 부모인 context가 변경되니까 그런거다.
부모인 context provider의 state가 변경은 하위 컴포넌트의 변경을 의미하여, 하위 모든컴포넌트다 렌더링 되게됨.

⚠️ **useContext 주의사항 with store**

useContext를 사용하는 컴포넌트들은 사용즉시, 강하게 커플링이 될 수 밖에 없다.

따라서 최대한 작게 또는 재사용될 수 있는 형태를 항상 고려해야된다.

**store관련 커플링이 생길텐데 어떻게들 설계하고 있는지?! ⇒ 프레젠테이션, 컨테이너 패턴?!**
### useReducer

상태값을 미리 정해놓은 시나리오에 따라 관리할 수 있다.
상태값을 미리 정해놓은 시나리오에 따라 관리.

state의 업데이트를 action으로 미리 정의된 dispatcher로만 제한한 것이다.
state의 업데이트를 action으로 미리 정의된 dispatcher로만 제한.

- action: state를 변경
- reducer: action을 정의하는 함수
- dispatcher: state를 업데이트하는 함수로 action을 넘겨준다.

```jsx
useReducer(state, initState, init);
// init = 지연 초기화로 useState의 지연 초기화와 같은 동작을함
// init(initState) 이캐됨
회사에서는 useReducer의 dispatcher로 모든 변경사항을 spreed 문법으로 병합함 어떻게보면 시나리오 없이 사용함으로 안티 패턴인가?
form이라는 것을 관리할때에 사용하는 것, 미리 정의된 dispatcher가 있는점으로 그렇다고 생각은 안함.
```
### useImperativeHandle
> **forwardRef는?**
> forwardRef는?
> props에 ref를 넣어 elemet에 접근하는 용도로 많이 쓰는데, ref를 전달하고 싶을 경우 사용함.
> ref가 예약어라 오류가 난다. **forwardRef**를 굳이 사용하는 이유는 일관성을 제공하기 위함이다.
> ref가 예약어라 오류가 난다. forwardRef를 굳이 사용하는 이유는 일관성을 제공하기 위함.
useImperativeHandle는 부모로 부터 전달 받은 ref를 수정할때 사용한다.
useImperativeHandle는 부모로 부터 전달 받은 ref를 수정할때 사용.
### useLayoutEffect
쓸일이 있을까…?
> 모든 DOM의 변경 후에 동기적으로 발생한다. useEffect와 형태나 사용예제가 동일하다.
### useLayoutEffect
DOM변경이란 렌더링을 의미하며, 브라우저에 실제로 변경 사항이 반영되는 시점을 의미하는것이 아니다.
모든 DOM의 변경 후에 동기적으로 발생.
이는 렌더링을 의미하며, 브라우저에 실제로 변경 사항이 반영되는 시점을 의미하는것이 아님.
리액트가 DOM 업데이트 → useLayoutEffect → 브라우저 변경사항 적용(**paint?!**) → useEffect
> 리액트가 DOM 업데이트 → useLayoutEffect → 브라우저 변경사항 적용(**paint?!**) → useEffect
동기적으로 발생함으로 리액트는 useLayoutEffect의 종료를 기다리고 다음을 진행한다는것을 의미한다. ⇒ 성능상 문제를 야기할 수 있다.
동기적으로 발생함으로 리액트는 useLayoutEffect의 종료를 기다리고 다음을 진행한다는것을 의미.
- DOM은 계산되었지만 화면에 반영되기 전에 하고 싶은 작업
- 동기적으로 처리되기에 성능 이슈가 있을 수 있음.
- DOM은 계산되었지만 화면에 반영되기 전에 하고 싶은 작업.
- 애니메이션, 스크롤 위치변경 등 ( = 자연스러운 UX 제공)
### 훅의 규칙 (rules-of-hooks)
1. 컴포넌트의 최상위에서 hooks를 호출해야하며, 반복 중첩문에서 호출할 수 없다.
1. 컴포넌트의 최상위에서 hooks를 호출해야하며, 조건, 반복문에서 호출할 수 없다.
1. 렌더링시 항상 동일한 순서로 훅이 호출되는것을 보장하기 위함이다.
2. 파이버객체의 linked-list로 저장되는데 조건, 반복문으로 예측할 수 없으면 오류 발생
2. 함수형 컴포넌트, hooks에서만 호출할 수 있다.
리액트의 훅은 파이버객체의 linked-list로 저장되는데 이때, 조건문 반복문또는 예측할 수 없는 순서로 되어있다면, 예측할 수 없는 동작을 초례한다.
## 사용자 정의 hook과 HOC
### 사용자 정의 hook
컴포넌트내부 사용 로직을 공유 및 재사용을 위함.
사용자 정의 hook만으로는 렌더링에 영향을 끼치지 못함.
### HOC
컴포넌트 렌더링에 영향을 미치는 경우 사용.
전달 받은 props를 변경하면 안됨. (추가는 되지만)
그래서 인증이 필요한 컴포넌트에 많이 사용
- 고차 컴포넌트: with\*\*\*
- 고차 컴포넌트가 사용된 컴포넌트: HOC\*\*\*
이런식으로 사용되는구만…
```jsx
//HOC 사용의 경우,
const HOCMe = withLoginComponet(() => (<></>));
// 회사는 AuthMiddleware를 사용해 로직 컴포넌트를 만들어 감싸는 형태로 사용중.
const Me = () => <AuthMiddleware><></></AuthMiddleware>);
```

0 comments on commit 3c3859b

Please sign in to comment.