React에서 오래된 클로저 피하기

여러분, 안녕하세요! 오늘 포스트에서는 코드에 좋지 않은 영향을 줄 수 있는 오래된 클로저에 대해 이야기하겠습니다.

먼저 클로저란 무엇입니까?

JavaScript의 클로저는 내부 함수가 외부 함수를 둘러싸고 나중에 사용할 수 있도록 외부 함수의 변수를 기억하는 경우입니다. 이것은 JavaScript의 어휘 환경 덕분입니다.

하지만 그게 무슨 뜻입니까? 아래 예를 살펴보겠습니다.

const createMultiplier = (multiplyBy) => {
  const multiplier = (toMultiply) => {
    return multiplyBy * toMultiply;
  }

  return multiplier;
}

const double = createMultiplier(2);
const ten = double(5);


위의 예에서 클로저를 사용하여 승수 함수를 만들었습니다. 더 설명하자면, multiplier 함수는 함수가 호출될 때를 기억하면서 외부 범위에서 multiplyBy 변수를 둘러쌉니다.

오래된 클로저는 내부 함수가 함수의 오래된 값을 기억하는 경우입니다. 예를 들면 다음과 같습니다.

let a = 0;
const add = () => {
  a += 1;
  const message = `Variable a is incremented to ${a}`;

  return () => {
    console.log(message);
  }
}

const log = add();
add();
add();

log(); // Outputs 1, Expected output: 3


위의 예에서 0으로 시작하는 숫자 변수를 만든 다음 여기에 1을 더하는 추가 함수를 만듭니다. 하지만 첫 번째 로그를 기록하지 않고 추가 기능을 3번 사용하고 로그할 때 3이 아닌 1을 기록합니다. 왜 그럴까요?

이를 스테일 클로저라고 합니다. 로그 함수는 a 변수의 오래된 버전을 감싸고 현재 있어야 하는 변수 대신 해당 변수를 기록했습니다.

이 문제를 어떻게 해결할까요?

let a = 0;
const add = () => {
  a += 1;

  return () => {
    const message = `Variable a is incremented to ${a}`;
    console.log(message);
  }
}

const log = add();
add();
add();

log(); // Outputs 3, Expected output: 3


이렇게 하면 로그 함수를 사용할 때 실행할 시간이 되면 현재 a 변수를 둘러싸서 올바른 값을 얻습니다.

여전히 더 많은 정보가 필요한 경우 게시할 때 확인할 수 있는 JavaScript의 클로저에 대한 다른 블로그를 게시할 것입니다.

이제 클로저가 React 코드에 영향을 미칠 수 있습니까? 아래 예를 확인하십시오.

import React, {useState, useEffect} from 'react';

const Timer = () => {
  const [time, setTime] = useState(0);
  const [isCounting, setIsCounting] = useState(false);

  useEffect(() => {
    if(isCounting) {
      const id = setInterval(() => {
        setTime(time + 0.1)
      }, 100);

      return () => {
        clearInterval(id)
      }
    }
  }, [isCounting])

  return (
    <div>
      The time is: {time.toFixed(1)}
      <br />
      <br />
      <button onClick={() => setIsCounting(!isCounting)}>
        {isCounting ? "Stop timer" : "Start Timer"}
      </button>
    </div>
  )
}


위의 예는 오래된 클로저입니다. 이유를 알 수 있습니까?

대답이 종속성 배열이라면 정답입니다! 반응 후크는 클로저 개념에 크게 의존하며 타이머가 처음 마운트될 때 시간의 초기 값은 0입니다. 따라서 setInterval의 콜백은 이를 캡처하고 반복해서 업데이트하려고 시도하므로 타이머는 값은 항상 0.1입니다.

어떻게 해결할 수 있습니까? 두 가지 솔루션이 있습니다.

import React, {useState, useEffect} from 'react';

const Timer = () => {
  const [time, setTime] = useState(0);
  const [isCounting, setIsCounting] = useState(false);

  useEffect(() => {
    if(isCounting) {
      const id = setInterval(() => {
        setTime(time + 0.1)
      }, 100);

      return () => {
        clearInterval(id)
      }
    }
  }, [isCounting, time]) // Added time as a dependency

  return (
    <div>
      The time is: {time.toFixed(1)}
      <br />
      <br />
      <button onClick={() => setIsCounting(!isCounting)}>
        {isCounting ? "Stop timer" : "Start Timer"}
      </button>
    </div>
  )
}


종속성 배열에 시간을 추가하여 시간이 변경될 때마다 React가 그에 따라 올바른 값으로 함수를 업데이트합니다. 그러나 두 번째 수정 사항이 있습니다.

import React, {useState, useEffect} from 'react';

const Timer = () => {
  const [time, setTime] = useState(0);
  const [isCounting, setIsCounting] = useState(false);

  useEffect(() => {
    if(isCounting) {
      const id = setInterval(() => {
        setTime(time => time + 0.1) // Give the setTime function a callback
      }, 100);

      return () => {
        clearInterval(id)
      }
    }
  }, [isCounting])

  return (
    <div>
      The time is: {time.toFixed(1)}
      <br />
      <br />
      <button onClick={() => setIsCounting(!isCounting)}>
        {isCounting ? "Stop timer" : "Start Timer"}
      </button>
    </div>
  )
}


useState 후크에서 set 함수에 콜백을 제공하면 react가 현재 상태에서 자동으로 상태를 업데이트할 수 있습니다. 또한 종속성 배열 안에 넣을 필요가 없습니다. 이는 때때로 혼란을 야기할 수 있고 틀림없이 더 깔끔해 보입니다.

결론



클로저는 JavaScript의 필수 부분이며 더 나은 코드를 작성하려면 클로저를 더 잘 이해해야 합니다. 물론 오래된 폐쇄를 피하십시오.

그리고 항상 그렇듯이 이 기사에 잘못된 정보가 있으면 알려주시면 바로잡겠습니다! 도움이 되었는지 아닌지에 대한 피드백을 듣고 싶습니다!

좋은 웹페이지 즐겨찾기