>  기사  >  웹 프론트엔드  >  반응: 오래된 폐쇄

반응: 오래된 폐쇄

王林
王林원래의
2024-08-21 06:19:02468검색

이번 게시물에서는 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

    위 내용은 반응: 오래된 폐쇄의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

    성명:
    본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.