React Hooks의useEffect에서 setInterval 등을 호출할 때state 등 값이 변하지 않는 문제의 해결 방법

먼저


다음 코드는state값이 변화할 때 어떤 동작을 할 것이라고 생각합니까?
본질이 아니기 때문에clearInterval 등의 처리는 생략되었다.
const [x, setX] = useState(0);
useEffect(() => {
  setInterval(() => {
    console.log(x);
  }, 1000);
}, []);
이 코드는 생각대로 움직이지 않고state의 값이 바뀌어도 0을 계속 출력합니다.
왜죠?JS의 폐쇄를 어느 정도 이해하는 사람이라면 알 것 같습니다.
구성 요소 함수가 호출될 때의 행동을 고려해 보세요.
첫 번째 호출에서 useState(0) 를 호출하면 x0 의 값을 입력합니다.useEffect의 리셋도 호출되었다.이때setInterval의 리셋은 x라는 변수를 포획했다.
두 번째 이후 호출 중 x 은 현재state (이 코드만 통해 구체적인 값을 알 수 없음)useEffect의 두 번째 매개 변수는 []이기 때문에 리셋을 호출하지 않습니다.
즉, setInterval 리셋된 x 은 항상 첫 번째 호출 때의 x 를 포획한다.
useState는 오해하기 쉬운 부분으로 N차x와 N차x는 완전히 다른 변수다.React가 잘 관리되기 때문에 같은 변수로 보이지만 JS로 보면 완전히 다른 것이다.여기가 포인트야.
그래서 setInterval의 리셋 포획된 x은 항상 처음으로 호출된 값, 즉 0이다.

해결 방법


그럼 어떻게 해결해야 되지?
사용 useRef 은 다음과 같습니다.
const [x, setX] = useState(0);
const refX = useRef(x);
useEffect(() => {
  refX.current = x;
}, [x]);
useEffect(() => {
  setInterval(() => {
    console.log(refX.current);
  }, 1000);
}, []);
왜 이렇게 하면 순조롭게 진행될 수 있습니까?
우선 2행~5행에는 시종일관x의 값을 refX.current에 넣는 코드가 적혀 있다.
한편 이번 setInterval 의 리턴은 x 이 아니라 refX 이다.
이번에도 N차 호출과 N차 호출refX은 완전히 다른 변수이지만 useRef 내부에서 느낌이 좋아서 refX의 값은 항상 고정되어 있다.그래서 전혀 다른 변수라도 문제가 생기지 않는다.

또한 x의 값이 refX에 있는 코드는 자주 사용되기 때문에 사용자 정의 연결화를 해야 한다.
export function useValueRef<T>(val: T) {
  const ref = React.useRef(val);
  React.useEffect(() => {
    ref.current = val;
  }, [val]);
  return ref;
}
이걸로 하면 이렇게 돼요.
const [x, setX] = useState(0);
const refX = useValueRef(x);
useEffect(() => {
  setInterval(() => {
    console.log(refX.current);
  }, 1000);
}, []);

경우에 따라 단순화된 솔루션


방금 포획할 변수를 인용하여 해결했습니다.
하지만 변수가 늘어나면 힘들어요.
그래서 이번에 우리는 함수를 참고한다.
const [x, setX] = useState(0);
const f = useValueRef(() => {
  console.log(x);
});
useEffect(() => {
  setInterval(() => {
    f.current();
  }, 1000);
}, []);
첫 번째 코드 예에서는 최신 x 을 캡처하는 데 사용되는 엔클로저를 항상 생성합니다.
그러나 useEffect의 두 번째 파라미터는 []이어서 사용하지 않고 버려졌다.
이번에는 이걸 효과적으로 이용해서 해결할게요.f.current는 항상 최신 x 엔클로저를 캡처합니다.f라는 이름이 오염되면 번거롭기 때문에 아래의 사용자 정의 연결고리를 만들어 청소하세요.
export function useEffectRef<T>(effect: (ref: React.MutableRefObject<T>) => void | (() => void | undefined), val: T, deps?: React.DependencyList) {
  const ref = useValueRef(val);
  React.useEffect(() => effect(ref), deps);
}
이걸로 하면 이렇게 돼요.

const [x, setX] = useState(0);
useEffectRef(f => {
  setInterval(() => {
    f.current();
  },1000);
}, () => {
  console.log(x);
}, []);
이렇게 하면 변수 증가도 쉽다.

좋은 웹페이지 즐겨찾기