Skip to content

Latest commit

 

History

History
515 lines (418 loc) · 10.2 KB

2020-4-30 Making a clock 204620.md

File metadata and controls

515 lines (418 loc) · 10.2 KB

Making a clock .....

Version 0 : this works....

const getCurrentDate = () => new Date();

[datetime, setDatetime] = useState(new Date())

tick(setDateTime(getCurrentDate()))

setInterval(tick,1000)

return {(
   <div>{datetime}</div>
 )
}

Version 1:

issue: why setInterval?

import React, { useReducer, useEffect, useCallback } from 'react';

import './Clock.scss';

const getCurrentDate = () => new Date();

const getMonthStr = (month) => {
  const monthNames = [
    'JAN',
    'MAR',
    'APR',
    'MAY',
    'JUN',
    'JUL',
    'AUG',
    'SEP',
    'OCT',
    'NOV',
    'DEC',
  ];
  return monthNames[month];
};

const getWeekStr = (week) => {
  const weekNames = [
    'Sunday',
    'Monday',
    'Tuesday',
    'Wednesday',
    'Thursday',
    'Friday',
    'Saturday',
    'Sunday',
  ];
  return weekNames[week];
};

const getDateString = (dateObj) =>
  `${dateObj.getDate()} ${getMonthStr(dateObj.getMonth())} ${getWeekStr(
    dateObj.getDay()
  )}`;

const pad = (number) => (+number < 10 ? `0${number}` : number);

const getTimeString = (dateObj) =>
  `${pad(dateObj.getHours())}:${pad(dateObj.getMinutes())}:${pad(
    dateObj.getSeconds()
  )}`;

const dateTimeReducer = (currState, action) => {
  clearInterval(currState.intervalId);
  return {
    date: getDateString(action.dateObj),
    time: getTimeString(action.dateObj),
  };
};

const Clock = (props) => {
  // cache tick function
  const tick = useCallback(() => {
    dispatch({ dateObj: getCurrentDate() });
  }, []);

  const [dateTimeState, dispatch] = useReducer(dateTimeReducer, {
    date: getDateString(getCurrentDate()),
    time: getTimeString(getCurrentDate()),
  });

  useEffect(() => {
    const id = setInterval(tick, 1000);
    return () => {
      //clean up
      clearInterval(id);
    };
  });

  return (
    <React.Fragment>
      {dateTimeState.date} {dateTimeState.time}
    </React.Fragment>
  );
};

export default Clock;

Version 2:

issue: what's better then settimeout?

  useEffect(() => {
    const id = setTimeout(tick, 1000);
    return () => {
      //clean up
      clearTimeout(id);
    };
  });

Version 3:

issue: how can i call requestAnimationFrame once only ? why do have have to keep creating the animation and cancel at every rerender.

import React, { useReducer, useEffect, useCallback } from 'react';

import './Clock.scss';

const getCurrentDate = () => new Date();

const getMonthStr = (month) => {
  const monthNames = [
    'JAN',
    'MAR',
    'APR',
    'MAY',
    'JUN',
    'JUL',
    'AUG',
    'SEP',
    'OCT',
    'NOV',
    'DEC',
  ];
  return monthNames[month];
};

const getWeekStr = (week) => {
  const weekNames = [
    'Sunday',
    'Monday',
    'Tuesday',
    'Wednesday',
    'Thursday',
    'Friday',
    'Saturday',
    'Sunday',
  ];
  return weekNames[week];
};

const getDateString = (dateObj) =>
  `${dateObj.getDate()} ${getMonthStr(dateObj.getMonth())} ${getWeekStr(
    dateObj.getDay()
  )}`;

const pad = (number) => (+number < 10 ? `0${number}` : number);

const getTimeString = (dateObj) =>
  `${pad(dateObj.getHours())}:${pad(dateObj.getMinutes())}:${pad(
    dateObj.getSeconds()
  )}`;

const dateTimeReducer = (currState, action) => {
  clearInterval(currState.intervalId);
  return {
    date: getDateString(action.dateObj),
    time: getTimeString(action.dateObj),
  };
};

const Clock = (props) => {
  // cache tick function
  const tick = useCallback(() => {
    dispatch({ dateObj: getCurrentDate() });
  }, []);

  const [dateTimeState, dispatch] = useReducer(dateTimeReducer, {
    date: getDateString(getCurrentDate()),
    time: getTimeString(getCurrentDate()),
  });

  useEffect(() => {
    const animation = window.requestAnimationFrame(tick, 1000);
    return () => cancelAnimationFrame(animation);
  });

  return (
    <React.Fragment>
      {dateTimeState.date} {dateTimeState.time}
    </React.Fragment>
  );
};

export default Clock;

v4

Issue: can you spot the bug?

import React, { useReducer, useEffect, useCallback } from 'react';

import './Clock.scss';

const getCurrentDate = () => new Date();

const getMonthStr = (month) => {
  const monthNames = [
    'JAN',
    'MAR',
    'APR',
    'MAY',
    'JUN',
    'JUL',
    'AUG',
    'SEP',
    'OCT',
    'NOV',
    'DEC',
  ];
  return monthNames[month];
};

const getWeekStr = (week) => {
  const weekNames = [
    'Sunday',
    'Monday',
    'Tuesday',
    'Wednesday',
    'Thursday',
    'Friday',
    'Saturday',
    'Sunday',
  ];
  return weekNames[week];
};

const getDateString = (dateObj) =>
  `${dateObj.getDate()} ${getMonthStr(dateObj.getMonth())} ${getWeekStr(
    dateObj.getDay()
  )}`;

const pad = (number) => (+number < 10 ? `0${number}` : number);

const getTimeString = (dateObj) =>
  `${pad(dateObj.getHours())}:${pad(dateObj.getMinutes())}:${pad(
    dateObj.getSeconds()
  )}`;

const dateTimeReducer = (currState, action) => {
  clearInterval(currState.intervalId);
  return {
    date: getDateString(action.dateObj),
    time: getTimeString(action.dateObj),
  };
};

const Clock = (props) => {
  // cache tick function
  const tick = useCallback(() => {
    dispatch({ dateObj: getCurrentDate() });
  }, []);

  const requestAnimation = useCallback((fn) => {
    const reqRef = requestAnimationFrame(fn);
    return reqRef;
  }, []);

  const [dateTimeState, dispatch] = useReducer(dateTimeReducer, {
    date: getDateString(getCurrentDate()),
    time: getTimeString(getCurrentDate()),
  });

  useEffect(() => {
    const requestRef = requestAnimation(tick);
    return () => cancelAnimationFrame(requestRef);
  });

  return (
    <React.Fragment>
      {dateTimeState.date} {dateTimeState.time}
    </React.Fragment>
  );
};

export default Clock;

v5

issue: the delay makes the second tick a few seconds slower then actual, so v6 will be v4, updates about 60 times per second

import React, { useReducer, useEffect, useCallback } from 'react';

import './Clock.scss';

const getCurrentDate = () => new Date();

const getMonthStr = (month) => {
  const monthNames = [
    'JAN',
    'MAR',
    'APR',
    'MAY',
    'JUN',
    'JUL',
    'AUG',
    'SEP',
    'OCT',
    'NOV',
    'DEC',
  ];
  return monthNames[month];
};

const getWeekStr = (week) => {
  const weekNames = [
    'Sunday',
    'Monday',
    'Tuesday',
    'Wednesday',
    'Thursday',
    'Friday',
    'Saturday',
    'Sunday',
  ];
  return weekNames[week];
};

const getDateString = (dateObj) =>
  `${dateObj.getDate()} ${getMonthStr(dateObj.getMonth())} ${getWeekStr(
    dateObj.getDay()
  )}`;

const pad = (number) => (+number < 10 ? `0${number}` : number);

const getTimeString = (dateObj) =>
  `${pad(dateObj.getHours())}:${pad(dateObj.getMinutes())}:${pad(
    dateObj.getSeconds()
  )}`;

const dateTimeReducer = (currState, action) => {
  clearInterval(currState.intervalId);
  return {
    date: getDateString(action.dateObj),
    time: getTimeString(action.dateObj),
  };
};

const Clock = (props) => {
  // cache tick function
  const tick = useCallback(() => {
    console.log('triggered');
    dispatch({ dateObj: getCurrentDate() });
  }, []);

  // credit Joelambert https://gist.github.com/joelambert/1002116#gistcomment-1953925
  const requestAnimation = useCallback((fn, delay) => {
    const start = new Date().getTime();
    let handle;
    const loop = () => {
      const current = new Date().getTime();
      const delta = current - start;
      delta >= delay ? fn() : (handle = requestAnimationFrame(loop));
    };
    handle = requestAnimationFrame(loop);
    return handle;
  }, []);

  // const requestAnimation = useCallback((fn) => {
  //   const reqRef = requestAnimationFrame(fn);
  //   return reqRef;
  // }, []);

  const [dateTimeState, dispatch] = useReducer(dateTimeReducer, {
    date: getDateString(getCurrentDate()),
    time: getTimeString(getCurrentDate()),
  });

  useEffect(() => {
    const requestRef = requestAnimation(tick, 1000);
    return () => cancelAnimationFrame(requestRef);
  });

  return (
    <React.Fragment>
      {dateTimeState.date} {dateTimeState.time}
    </React.Fragment>
  );
};

export default Clock;

The final (not perfect) version

import React, {useCallback, useEffect, useReducer} from 'react';

import './Clock.scss';
import {getCurrentDate, getDateString, getTimeString} from "../../Utils/DateTimeUtils";


const dateTimeReducer = (currState, action) => {
  return {
    date: getDateString(action.dateObj),
    time: getTimeString(action.dateObj),
  };
};

const Clock = (props) => {

  const [dateTimeState, dispatch] = useReducer(dateTimeReducer, {
    date: getDateString(getCurrentDate()),
    time: getTimeString(getCurrentDate()),
  });

  // cache tick function
  const tick = useCallback(() => {
    setTimeout(() => {
      dispatch({dateObj: getCurrentDate()});
      return requestAnimationFrame(tick)
    },500)
  }, []);


  useEffect(() => {
    console.log('this is run')
    const requestRef = requestAnimationFrame(tick)
    return () => cancelAnimationFrame(requestRef);
  },[]);

  return (
      <div className={`clock ${props.clockClassNames&&props.clockClassNames}`}>
        {console.log('i am re-rendered')}
        <div className={`clock-time ${props.timeClassNames && props.timeClassNames}`}>
          {dateTimeState.time}
        </div>
        <div className={`clock-date ${props.dateClassNames && props.dateClassNames}`}>
          {dateTimeState.date}
        </div>
      </div>
  );
};

export default Clock;
  • datetimeutil.js
const pad = (number) => (+number < 10 ? `0${number}` : number);
export const getCurrentDate = () => new Date();
const getWeekStr = (week) => {
  const weekNames = [
    'Sunday',
    'Monday',
    'Tuesday',
    'Wednesday',
    'Thursday',
    'Friday',
    'Saturday',
    'Sunday',
  ];
  return weekNames[week];
};
const getMonthStr = (month) => {
  const monthNames = [
    'Jan',
    'Feb',
    'Mar',
    'Apr',
    'May',
    'Jun',
    'Jul',
    'Aug',
    'Sep',
    'Oct',
    'Nov',
    'DEC',
  ];
  return monthNames[month];
};
export const getDateString = (dateObj) =>
    `${dateObj.getDate()} ${getMonthStr(dateObj.getMonth())} ${getWeekStr(
        dateObj.getDay()
    )}`;
export const getTimeString = (dateObj) =>
    `${pad(dateObj.getHours())}:${pad(dateObj.getMinutes())}`;