[TIL] 0328

yoon Y·2022년 3월 29일
0

2022 - TIL

목록 보기
65/109

WaffleCard Refactoring

CardList Animation 구현 시도

useInterval 재실행, 중지 시 재렌더링 방지하기

문제점/결과
isPlayMove상태를 의존하기 때문에 useInterval(스크롤 애니메이션)이 재실행, 중지될 때마다 리렌더링이 발생한다. 이를 막기 위해 ref로 변경 시도했지만 원하는대로 동작하지 않았다..

이유
isPlayMove 상태가 바뀔 때마다 리렌더링되어, 바뀐 상태값을 이용한 새로운 인자값으로 useInterval이 다시 실행되어야하는데 ref.current는 변경되어도 리렌더링이 되지 않기 때문에 useInterval이 재실행되지 않는다.

// 관련 코드만 간추림 //

// WaffleCardsList.tsx
const WaffleCardsList = ({
  ...props
}: WaffleCardsListProps) => {
  ...
  const {
    isPlaying,
    setIsPlaying,
    isIntersecting,
    setObserveTarget,
    moveScrollToFront,
    moveScrollToBack,
  } = useScrollAnimation(cardsListRef.current, [type]);
// useScrollAnimation.ts
const useScrollAnimation = (
  ...
): ReturnTypes => {
  
  const [isPlaying, setIsPlaying] = useState(true);
  
  useInterval(
    () => {
      if (!(observeTargetDom instanceof Element)) return;
      observeTargetDom.scrollLeft += 1;
    },
    isPlaying ? 15 : null, // null일 시 setInterval중지
  );

  return {
    isPlaying,
    setIsPlaying,
    isIntersecting,
    setObserveTarget,
    moveScrollToFront,
    moveScrollToBack,
  }; 
  ...

onMouseOver, onMouseOut 이벤트 버블링 버그

문제점
CardList에 onMouseOver, onMouseOut이벤트를 걸었을 때, 이벤트가 발생되면 이벤트핸들러콜백이 계속해서 실행됐다. 스크롤 이벤트와 같은 경우인줄 알고 throttle을 걸어봤지만 같은 현상이었다.
이유는 이벤트 버블링 때문이었다. 가장 상위 컴포넌트에 이벤트를 걸었을 경우, 하위 자식들에게도 적용이 되기 때문에 이벤트가 자식들 수 만큼 여러 번 발생하는 것이었다. (사실 자식들 수만큼이 아니라 수없이 발생됐지만 정확한 이유는 모르겠다.. 버블링을 이유로 뭔가 꼬인 것 같다)

해결책
버블링이 발생하지 않는 onMouseEnter, onMouseLeave를 사용해서 해결했다!

참고자료


JS 실행 컨텍스트 학습

스코프

함수 레벨: var, function
블록 레벨: let const

함수 레벨의 선언들은 함수 내에서 세세한 블록 범위를 적용할 수 없다.
그래서 의도치 않은 문제가 생길 수 있다.
변수는 let, const, 함수는 선언식 보다는 표현식으로 사용하는 게 명확하다.

실행 컨텍스트

함수가 실행될 때 생성되는 객체로, 함수 실행에 필요한 정보들을 가지고 있다.
전역코드(전역 컨텍스트), 함수, 코드블럭이 실행될 때 만들어진다.

컨텍스트 구성

크게 3가지로 나뉜다.

VariavleEnvironment: 선언 시점의 LexicalEnvironment의 스냅샷, 변경사항은 반영x
Lexical Environment: 변경사항이 실시간으로 반영됨
ThisBinding: this 식별자가 바라보고 있는 대상 객체

Eviroment 내부는 2개로 나뉜다.

environmentRecord : 현재 컨텍스트 내의 식별자들에 대한 정보
outer-EnvironmentReference : 상위 컨텍스트 참조(외부 환경 정보)

js 코드 실행과정

  1. 실행 컨텍스트 객체 생성
  2. 호이스팅 (선언된 변수, 함수 선언 수집)
  3. 한 줄씩 코드 실행
  4. 실행되 때 필요한 값들을 recode에서 가져오거나 스코프체인으로 부모 컨텍스트를 참조
  5. 코드 종료 시 컨텍스트 제거(가비지 콜렉터) *클로저가 포함된 컨텍스트는 제거되지 않고 어딘가에 저장됨

호이스팅

함수 실행 전 선언된 변수나 함수를 실행 컨텍스트에 등록(또는 초기화까지) 하는 것

var: 선언과 동시에 초기화되어 메모리까지 할당됨 초기값 ? 초기값 : undefind
let/const: 선언시에 TDZ상태, 실행 시 초기화/할당 됨
function: 선언, 초기화, 할당 동시에 진행됨

선언 후 실행하는 일반적인 흐름대로 코딩하려면 let, const, 함수 표현식을 사용하는게 좋음

클로저

외부함수가 내부함수를 반환해 종료되었을 때, 반환된 내부함수가 외부함수 내부 값을 사용할 수 있는 것. 또는 사용하는 내부 값 자체
클로저를 가지는 외부함수의 실행 컨텍스트는 사라지지 않는다.

반환된 내부함수는 자신이 선언 시 구성됐던 외부환경을 참조할 수 있다.

실행 중 함수 - 종료된 함수 - 종료된 함수 - 반환된 함수

의 구조에서 반환된 함수가 실행될 때, 내부에서 사용된 값을 찾기 위해 클로저 체인으로 부모들의 컨텍스트를 참조한다.

이때 해당 값이 실행 중 함수에 속해 있으면 값이 변동될 수 있지만,
종료된 함수에 속해 있다면 선언 시의 고정 값만 사용할 수 있는데 이 값이 클로저이다.

for문 사용 시 i값을 let으로 선언해줘야하는 이유이다.
실행 중인 함수 내의 변동돨 수 있는 전역 값을 사용하는 게 아닌,
종료된 for문 블럭 내의 처음 초기화된 값을 고정해서(저장해서) 사용하기 위해서이다.
각 for문 블럭 내의 로직은 실행되는 시점의 i값을 이용해야하기 때문

profile
#프론트엔드

0개의 댓글