"일꾼이 일을 잘하려면 먼저 도구를 갈고 닦아야 한다." - 공자, 『논어』.
첫 장 > 프로그램 작성 > 반응: 오래된 폐쇄

반응: 오래된 폐쇄

2024-09-02에 게시됨
검색:103

이 게시물에서는 useState 후크 React 앱에서 클로저를 생성하는 방법을 보여드리겠습니다.

클로저가 무엇인지 설명하지 않겠습니다. 이 주제에 대한 리소스가 많고 반복하고 싶지 않기 때문입니다. @imranabdulmalik의 이 기사를 읽어보시길 권합니다.

간단히 말하면, 클로저는 (Mozilla에서):

... 주변 상태(어휘 환경)에 대한 참조와 함께 묶인(동봉된) 함수의 조합입니다. 즉, 클로저를 사용하면 내부 함수에서 외부 함수 범위에 액세스할 수 있습니다. JavaScript에서는 함수가 생성될 때마다 함수 생성 시간.

에 클로저가 생성됩니다.

어휘적 환경이라는 용어에 익숙하지 않은 경우를 대비해 @soumyadey 또는 이 글을 읽어보세요.

문제

React 애플리케이션에서는 useState 후크로 생성된 구성 요소 상태에 속하는 변수의 클로저를 실수로 생성할 수 있습니다. 이런 일이 발생하면 오래된 클로저 문제에 직면하게 됩니다. 즉, 그 동안 변경되어 더 관련성이 없는 상태의 이전 값을 참조할 때입니다.

POC

저는 setTimeout 메소드 콜백의 클로저에서 닫힐 수 있는 카운터(상태에 속함)를 증가시키는 것이 주요 목표인 Demo React 애플리케이션을 만들었습니다.

간단히 말하면 이 앱은 다음을 수행할 수 있습니다.

  • 카운터 값 표시
  • 카운터 1씩 증가
  • 타이머를 시작하여 5초 후에 카운터를 1씩 증가시킵니다.
  • 카운터 10씩 증가

다음 그림에서는 카운터가 0인 앱의 초기 UI 상태를 보여줍니다.

React: stale closure

카운터 폐쇄를 세 단계로 시뮬레이션합니다.

  1. 카운터 1씩 증가

React: stale closure

  1. 5초 후에 1씩 증가하도록 타이머 시작

React: stale closure

  • 시간 초과가 트리거되기 전에 10씩 증가 React: stale closure

5초 후 카운터 값은 2입니다.

React: stale closure

카운터의 예상 값은 12여야 하지만 2를 얻습니다.

이런 일이 발생하는 이유는 setTimeout에 전달된 콜백에서 카운터 폐쇄를 생성했고 시간 초과가 트리거될 때 카운터를 다음부터 설정하기 때문입니다. 이전 값(1).

setTimeout(() => {
        setLogs((l) => [...l, `You closed counter with value: ${counter}\n and now I'll increment by one. Check the state`])
        setTimeoutInProgress(false)
        setStartTimeout(false)
        setCounter(counter   1)
        setLogs((l) => [...l, `Did you create a closure of counter?`])

      }, timeOutInSeconds * 1000);

앱 구성 요소의 전체 코드를 따릅니다.

function App() {
  const [counter, setCounter] = useState(0)
  const timeOutInSeconds: number = 5
  const [startTimeout, setStartTimeout] = useState(false)
  const [timeoutInProgress, setTimeoutInProgress] = useState(false)
  const [logs, setLogs] = useState>([])

  useEffect(() => {
    if (startTimeout && !timeoutInProgress) {
      setTimeoutInProgress(true)
      setLogs((l) => [...l, `Timeout scheduled in ${timeOutInSeconds} seconds`])
      setTimeout(() => {
        setLogs((l) => [...l, `You closed counter with value: ${counter}\n and now I'll increment by one. Check the state`])
        setTimeoutInProgress(false)
        setStartTimeout(false)
        setCounter(counter   1)
        setLogs((l) => [...l, `Did you create a closure of counter?`])

      }, timeOutInSeconds * 1000);
    }
  }, [counter, startTimeout, timeoutInProgress])

  function renderLogs(): React.ReactNode {
    const listItems = logs.map((log, index) =>
      
  • {log}
  • ); return
      {listItems}
    ; } function updateCounter(value: number) { setCounter(value) setLogs([...logs, `The value of counter is now ${value}`]) } function reset() { setCounter(0) setLogs(["reset done!"]) } return (

    Closure demo


    Counter value: {counter}


    Follow the istructions to create a closure of the state variable counter

    1. Set the counter to preferred value
    2. Start a timeout and wait for {timeOutInSeconds} to increment the counter (current value is {counter})
    3. Increment by 10 the counter before the timeout

    { renderLogs() }
    ); } export default App;

    해결책

    이 솔루션은 렌더링에 필요하지 않은 값을 참조할 수 있는 useRef 후크 사용을 기반으로 합니다.

    따라서 App 구성 요소에 다음을 추가합니다.

    const currentCounter = useRef(counter)
    

    그런 다음 아래와 같이 setTimeout의 콜백을 수정합니다.

    setTimeout(() => {
            setLogs((l) => [...l, `You closed counter with value: ${currentCounter.current}\n and now I'll increment by one. Check the state`])
            setTimeoutInProgress(false)
            setStartTimeout(false)
            setCounter(currentCounter.current   1)
            setLogs((l) => [...l, `Did you create a closure of counter?`])
    
          }, timeOutInSeconds * 1000);
    

    현재 값을 증가시키기 전에 기록하므로 콜백은 카운터 값을 읽어야 합니다.

    값을 읽을 필요가 없는 경우 기능 표기를 사용하여 카운터를 업데이트하면 카운터가 닫히는 것을 방지할 수 있습니다.

    seCounter(c => c   1)
    

    자원

    • Dmitri Pavlutin은 React Hooks 사용 시 오래된 클로저에 주의하세요.
    • Imran Abdulmalik JavaScript 클로저 마스터하기: 종합 가이드
    • JavaScript의 Keyur Paralkar 어휘 범위 – 초보자 가이드
    • React의 Souvik Paul 스테일 폐쇄
    • Soumya Dey JavaScript의 어휘 범위 및 클로저 이해
    • Subash Mahapatra stackoverflow
    릴리스 선언문 이 글은 https://dev.to/animusna/react-stale-closure-81a?1 에서 복제되었습니다.1 침해 내용이 있는 경우, [email protected]으로 연락하여 삭제하시기 바랍니다.
    최신 튜토리얼 더>

    부인 성명: 제공된 모든 리소스는 부분적으로 인터넷에서 가져온 것입니다. 귀하의 저작권이나 기타 권리 및 이익이 침해된 경우 자세한 이유를 설명하고 저작권 또는 권리 및 이익에 대한 증거를 제공한 후 이메일([email protected])로 보내주십시오. 최대한 빨리 처리해 드리겠습니다.

    Copyright© 2022 湘ICP备2022001581号-3