역주 : 시작하기 전에
짧지만 유익했던 글이었습니다!
저도 이런 경험이 있었던 것 같은데, 읽어보시면 도움이 될 수 있을 것 같네요!
useState, useRef와 함께 이벤트 리스너에서 상태에 접근하는 방법
원제 : Accessing React State in Event Listeners with useState and useRef hooks
컴포넌트에서 이벤트 리스너와 함께 리액트 훅을 사용하고 있다면, 해당 이벤트 리스너의 콜백 함수는 최신 상태값을 읽어들일 수 없다는 문제가 있습니다. 이 문제는
useRef
훅을 코드에 포함해 해결할 수 있습니다.아래의 간단한 예시 코드에서는, 더블 클릭 이벤트가 발생할 때 상태값을 읽으려 시도합니다.
import React from "react"; import ReactDOM from "react-dom"; import "./styles.css"; function App() { const [myState, setMyState] = React.useState(0); const listener = () => { console.log(`state in handler: ${myState}`); // state in handler: 0 }; React.useEffect(() => { window.addEventListener("dblclick", listener); }, []); return ( <div className="App"> <h1>State: {myState}</h1> <button onClick={() => setMyState(myState + 1)}>update state</button> </div> ); } const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement);
코드를 실행해보면 이벤트 리스너가 초기 상태값만 읽어들이는 것을 알 수 있는데요, 따라서 스택 오버플로우 글에서 설명된 것처럼 (역주 : 더블 클릭을 할때) 언제나
state in handler : 0
이라는 로그를 확인할 수 있습니다.이는 이벤트 리스너가 초기 렌더링 상태에 귀속되고 이어지는 리렌더링으로 업데이트되지 않기 때문입니다.
그럼 이 문제는 어떻게 해결할 수 있을까요?
this.state.myState
처럼 클래스 컴포넌트를 활용하면 항상 최신 상태값을 사용하도록 할 수 있기는 합니다.그러나 훅을 사용해 이를 해결하고 싶다면,
useRef
훅을 사용하는 방법이 있습니다.
다음 코드는 useRef
훅을 통해 이벤트 리스너가 참조하는 상태값을 업데이트하는 예시입니다.import React from "react"; import ReactDOM from "react-dom"; import "./styles.css"; function App() { const [myState, _setMyState] = React.useState(0); const myStateRef = React.useRef(myState); const setMyState = data => { myStateRef.current = data; _setMyState(data); }; const listener = () => { console.log(`state in handler: ${myStateRef.current}`); // state in handler: 1 // state in handler: 2 // etc... }; React.useEffect(() => { window.addEventListener("dblclick", listener); }, []); return ( <div className="App"> <h1>State: {myState}</h1> <button onClick={() => setMyState(myState + 1)}>update state</button> </div> ); } const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement);
이 코드에서는
myStateRef
라는 useRef
훅을 useState
로 만든 myState
값으로 초기화하고 있습니다.
그리고 myState
와 myStateRef.current
의 값을 동시에 업데이트하는 함수를 추가했습니다.이제 이벤트 리스너에서는
myStateRef.current
값을 사용함으로써 최신 값을 읽어들일 수 있습니다.