개인 프로젝트 agenda (이제는 pilzAgenda가 되어버린) 를 진행하면서 여러 문제에 맞닥뜨리게 되었다.
그 중 하나가 시간을 나타내는 문제.
시간? 그거 그냥 api받아오던가 현재 시간 받아와서 그려주면 되는거 아닌가?
하고 안일하게 생각했던 내 자신을 반성하게 된 어제 저녁을 계기로 react에서 setInterval을 어떻게 사용해야 하는지에 대해서 글을 작성해보기로 했다.
만약 내가 바닐라 자바스크립트를 이용해서 프로젝트를 만들고 있었다면 정말로 setInterval을 이용했을지도 모른다.
일단 원하는 결과는 화면에 그려지기에.
하지만 react는 누구나 알다시피 props나 state의 변화가 없으면 재렌더링되지 않는다.
또한 너무 많은 재렌더링은 react의 효율을 떨어뜨린다
=> setInterval로 state를 변경해가며 시간을 찍어내는 일반적인 방법은 쓸 수 없다는 것
1. 일단 react로 setInterval을 사용해서 시간을 찍어 낼 때 일어나는 문제들에 대해서 설명
2. setInterval 대신 사용할 수 있는 대안들에 대해 소개
🤦♀️
setIntserval(()=>{}, ms). 일정시간마다 콜백함수 안에 있는 작업을 수행한다.
문제
1. 시간 보장의 문제
내가 사용자에게 보여주고 싶은 것이 '현재 시간'이니만큼 내가 작성한 코드에 시간은 아주 중요한 요소이다.
하지만 setInterval은 실제 시간을 반영하는 것이 아니라 컴퓨터가 측정한 ms초를 반영하여 작업을 수행하는 것이다.
즉 컴퓨터가 함수를 실행시키는 시간 + setInterval의 딜레이 시간이 포함되기 때문에 아주 약간의 시간차가 생길 수 있다는 것
하지만 내 프로젝트에서 시간 보장의 문제는 그렇게 중요한 문제가 아니었다.
1. 아주 정확한 시간을 1초단위로 보여줄 필요가 없고
2. setInterval을 사용해서 시간을 찍어주는 것이 아니라 정확히 측정된 시간을 1초마다 가져오는 것이었기 때문에
문제
2. 임피던스 불일치
리엑트 컴포넌트는 렌더링 하며 상태변화를 한 번에 표현한다.
그러나 interval은 interval을 없애지 않는 이상 무엇도 변경할 수 없다.
react는 props나 state가 바뀌면 렌더링된다.
=> 렌더링이 될 때마다 변한 props나 state를 이용하여 렌더링된다.
하지만 setInterval은 직접 교체해주기 전까지는 처음 설정한 props나 state를 계속 참조한다.
class Counter extends React.Component {
state = {
count: 0,
delay: 1000,
};
componentDidMount() {
this.interval = setInterval(this.tick, this.state.delay);
}
// 1. 컴포넌트가 Mount되면 this.interval에 setInterval을 설정해준다.
// 이 setInterval함수 안에 있는 tick은 state의 count를 1씩 늘리는 함수.
componentDidUpdate(prevProps, prevState) {
if (prevState.delay !== this.state.delay) {
clearInterval(this.interval);
this.interval = setInterval(this.tick, this.state.delay);
}
}
// 2. 컴포넌트는 업데이트되었다는 것이 확인되고
// 2-1) delay를 동적으로 조절할 경우(delay값이 변경되었을 경우)(if) this.interval을 clear로 끝내주고
// 새로운 delay를 넣어서 다시 this.interval에 setInterval을 설정해준다.
componentWillUnmount() {
clearInterval(this.interval);
}
// 3. 컴포넌트가 unmount될 때 clearInterval을 해준다.
tick = () => {
this.setState({
count: this.state.count + 1
});
}
handleDelayChange = (e) => {
this.setState({ delay: Number(e.target.value) });
}
render() {
return (
<>
<h1>{this.state.count}</h1>
<input value={this.state.delay} onChange={this.handleDelayChange} />
</>
);
}
}
==
setState로 state의 값을 바꾸어주면 리렌더링이 일어나지만 => state의 초기값으로 계속 랜더링되는 문제가 발생한다.
const [count, setCount] = useState(0)
useEffect(()=>{
setInterval(()=>{
setCount(count+1);
},1000);
console.log(count)
},[])
// 1, 2, 3, ... 이 아니라
// 1, 1, 1 로 1만 찍힌다 => state의 초기값으로 계속 렌더링되기 때문에
하지만 해결할 수는 있다
바로 updater function을 사용하면 된다
const [count, setCount] = useState(0)
useEffect(()=>{
setInterval(()=>{
setCount(prevC => prevC + 1); // updater function
},1000);
console.log(count)
},[])
그러면 이제 updater function을 쓰면 될까..??!
아니다
props가 변경되는 경우에는 역시 해결할 수 없다
해결
그래서 useInterval이 등장했다.
이 useInterval은 interval이 unmount상태가 되기 전에 clearInterval을 해준다.
그리고 callback을 저장할 때 state 대신 ref를 사용해준다.
useInterval 버전
function Counter() {
let [count, setCount] = useState(0);
let [delay, setDelay] = useState(1000);
useInterval(() => {
setCount(count + 1);
}, delay);
// ** 인풋에 변화(onChange)가 일어났을 때 위 setInterval처럼 clearInterval을 하고 새로운
// 변화 값(delay)를 넣어주지 않아도 알아서 인터벌을 새로 설정하는 것을 볼 수 있다
function handleDelayChange(e) {
setDelay(Number(e.target.value));
}
return (
<>
<h1>{count}</h1>
<input value={delay} onChange={handleDelayChange} />
</>
);
}
https://mingule.tistory.com/65
https://overreacted.io/making-setinterval-declarative-with-react-hooks/
https://iborymagic.tistory.com/96
'✍ 공부 > React' 카테고리의 다른 글
React에서 styled-component 이용하기 + typescript (1) | 2022.03.28 |
---|---|
BrowserRouter HashRouter 차이 + BrowserRouter 배포시 흰 화면 문제 (0) | 2022.03.14 |
🔧[ recoil ] recoil로 react 상태관리 하기 (0) | 2022.01.26 |
🥜 [ React ] react 학습내용 나름대로 정리하기 1 (0) | 2022.01.21 |
🔧[ React Query ] 리엑트 쿼리 사용법 (0) | 2022.01.19 |