React - 특정 시간이 될 경우 state 초기화 로직 구현(moment.js, useInterval, useEffect)

jiny·2022년 8월 6일
3

React

목록 보기
1/11
post-thumbnail
post-custom-banner

구현한 목적

  • 프로젝트에서 일 -> 월로 넘어갈 경우 모든 요일의 체크 state가 초기화 되도록 기능요구사항을 백엔드 측과 협의했기 때문에 목업 데이터로 테스트 해보고자 했다.

moment.js

  • 시간이 포함된 데이터를 받아 조작해야 할 경우 가장 많이 사용되는 편리한 라이브러리
  • 이번 로직의 경우 현재 시간을 받아오기 위해 해당 라이브러리 사용

moment.js 설치

npm i moment
  • 다음과 같이 라이브러리를 설치

moment.js 사용

import moment from "moment";

// 오늘 요일의 값을 useState로 저장
const[seconds, setSeconds] = useState(moment().format('HH:mm:ss'));
  • moment()를 호출하게 되면 현재 날짜, 시간을 얻을 수 있다.
  • format()은 지정된 형식으로 변환해 준다.
  • 시, 분, 초를 string의 형태로 값 변경을 위해 useState를 통해 seconds에 저장했다.
// 날짜를 얻어오기 위한 dayOfWeek
const dayOfWeek = new Date(moment().format('YYYY-MM-DD')).getDay();
  • moment().format에 있는 날짜에 대한 요일을 Number의 형태로 저장한다.

useInterval

npm i react-use
  • react-use 라이브러리를 설치한다.
import { useInterval } from 'react-use';

useInterval(()=>{
    setSeconds(moment().format('HH:mm:ss'));
}, 1000);
  • useInterval을 통해 second의 현재 시, 분, 초 값을 1초 단위로 업데이트
  • useState의 setState(setSeconds) 함수를 통해 구현

  • 콘솔로 seconds를 출력하면 실시간을 출력하는 것을 확인 할 수 있음
// useInterval.d.ts

declare const useInterval: (callback: Function, delay?: number | null | undefined) => void;
export default useInterval;


// useInterval.js

Object.defineProperty(exports, "__esModule", { value: true });
var react_1 = require("react");
var useInterval = function (callback, delay) {
    var savedCallback = react_1.useRef(function () { });
    react_1.useEffect(function () {
        savedCallback.current = callback;
    });
    react_1.useEffect(function () {
        if (delay !== null) {
            var interval_1 = setInterval(function () { return savedCallback.current(); }, delay || 0);
            return function () { return clearInterval(interval_1); };
        }
        return undefined;
    }, [delay]);
};
exports.default = useInterval;
  • 라이브러리에 있는 함수는 다음과 같이 declaration file, js파일로 선언되어있다.
  • 이는 js파일은 ts파일이 읽지 못하기 때문에 d.ts 파일을 통해 함수에 들어오는 인자의 타입과 return 타입을 명시해주기 때문에 가능

구현

interface IWeekInfo {
    id : number
    date : string,
    state : boolean
}

// week state
const [week, setWeek] = useState<IWeekInfo[]>([
    {id: 0, date: '월', state: false},
    {id: 1, date: '화', state: false}, 
    {id: 2, date: '수', state: false}, 
    {id: 3, date: '목', state: false}, 
    {id: 4, date: '금', state: false}, 
    {id: 5, date: '토', state: false}, 
    {id: 6, date: '일', state: false}
]);
  • Weekly Check에 있는 각 컴포넌트의 상태를 객체 배열의 형태로 저장하였다.
  • 사용자가 클릭할 시 해당 date에 해당하는 state 값을 true로 변경함
// check.tsx

useEffect(() => {
    (seconds === '00:00:00' || dayOfWeek === 1) && setWeek(
        (week) => {
            let newWeek = [
                {id: 0, date: '월', state: false},
                {id: 1, date: '화', state: false}, 
                {id: 2, date: '수', state: false}, 
                {id: 3, date: '목', state: false}, 
                {id: 4, date: '금', state: false}, 
                {id: 5, date: '토', state: false}, 
                {id: 6, date: '일', state: false}
            ]
            return newWeek;
        }
    );
},[seconds]);
  • 배열에 있는 객체들의 값을 ts는 모르기 때문에 다음과 같이 interface를 작성하였다.
  • 실시간으로 tracking하고 있는 seconds가 만약 00:00:00이며, 요일이 일->월로 변경될 경우 setWeek를 통해 월~일의 state를 false로 초기화
  • 이는 월~일의 Circle 컴포넌트를 모두 체크되지 않은 컴포넌트로 초기화하기 위함
  • useEffect의 두 번째 인자로 seconds를 넣어 seconds가 변경되는 것에 대해 한번만 실행하도록 함.

반드시 seconds를 두 번째 인자로 넣어야하는 이유

Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render

  • 두 번째 인자를 넣지 않을 때의 useEffect는 재렌더링이 발생할 때마다 useEffect가 실행되는 건데 그럴 경우 00:00:00일 경우 useInterval이 다음과 같이 한 없이 실행 되는 문제 발생
  • useEffect가 계속 실행되는거 자체가 좋은 코드는 아니다.
  • 만약, setWeek가 특정 요일의 state를 변경해야하는 로직이라면 다음과 같이 true, false, true 이런 식으로 예측할 수 없게 변경될 수 있음.

  • 코딩애플님 인강에서 말해줬던 내용을 리마인드 하게 되었다.
  • useEffect의 2번째 인자로 state 값을 넣어주면 특정 state가 변경시에만 실행되기 때문에, 00:00:00 & 요일이 일 -> 월로 변경되면 seconds의 값이 한번만 변경되어 저 문제가 발생하지 않는다.

실행 결과

  • 예상한 대로 월요일 00:00:00이 되면 초기화가 되는 것을 확인
post-custom-banner

0개의 댓글