I am following this tutorial to make a javascript calendar in react
I have a working calendar UI with the following code
// https://medium.com/@nitinpatel_20236/challenge-of-building-a-calendar-with-pure-javascript-a86f1303267d import { useState, useEffect, useRef, useMemo } from 'react' import type { NextPage } from 'next' import Head from 'next/head' import styles from '../styles/Home.module.scss' const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] // console.log('render') const Home: NextPage = () => { const today = useMemo(() => new Date(), []); const [currentMonth, setCurrentMonth] = useState(today.getMonth()) const [currentYear, setCurrentYear] = useState(today.getFullYear()) const calendarBodyRef = useRef<HTMLDivElement>(null) // check how many days in a month code from https://dzone.com/articles/determining-number-days-month const daysInMonth = (iMonth: number, iYear: number) => { return 32 - new Date(iYear, iMonth, 32).getDate() } useEffect(() => { const showCalendar = (month: number, year: number) => { const firstDay = (new Date(year, month)).getDay() const calendarBody = calendarBodyRef.current // creating all cells let date = 1; for (let i = 0; i < 6; i++) { // creates a table row const row = document.createElement("div") row.classList.add(styles.row) //creating individual cells, filing them up with data. for (let j = 0; j < 7; j++) { if (i === 0 && j < firstDay) { const cell = document.createElement("div") cell.classList.add(styles.cell) const cellText = document.createTextNode("") cell.appendChild(cellText) row.appendChild(cell) } else if (date > daysInMonth(month, year)) { break } else { const cell = document.createElement("div") cell.classList.add(styles.cell) const cellText = document.createTextNode(String(date)) if (date === today.getDate() && year === today.getFullYear() && month === today.getMonth()) { cell.classList.add(styles.currentDay) } // color today's date cell.appendChild(cellText) row.appendChild(cell) date++ } } if (calendarBody) { calendarBody.appendChild(row) // appending each row into calendar body. } } } showCalendar(currentMonth, currentYear) }, [currentMonth, currentYear, today]) return ( <> <Head> <title>Calendar Budget App</title> <meta name="description" content="Generated by create next app" /> <link rel="icon" href="/favicon.ico" /> </Head> <div className={styles.calendarWrap}> <h2 className={styles.monthTitle}>{months[currentMonth]} {currentYear}</h2> <div className={styles.daysWrap}> <span>Sun</span> <span>Mon</span> <span>Tue</span> <span>Wed</span> <span>Thu</span> <span>Fri</span> <span>Sat</span> </div> <div ref={calendarBodyRef} className={styles.calendarBody}> </div> </div> </> ) } export default Home
However in development, my calendar is made twice
I assume this is because of React.StrictMode
in development as useEffect
seems to be running twice. If I run npm run build
and npm start
to mimic production, I only see one calendar.
Is there still a way for my calendar to appear once in development?
Advertisement
Answer
The problem you’re having is that your dependencies in the useEffect are changing on the new render cycle and so showCalendar is triggered multiple times.
To keep your code and only run it once, you should just be able to define the variables:
const today = ... const currentMonth = ... const currentYear = ...
and remove the dependencies from the useEffect, as they don’t change:
showCalendar(currentMonth, currentYear) }, [])
However the useEffect is entirely unecessary, and your approach is strange.
Just do:
<div ref={calendarBodyRef} className={styles.calendarBody}> {months.map(month => { return <div>[...your logic here]<div> })} </div>
or:
const getMonthElement = (month) => { return <div>[your month logic]<div> } ... return( <div ref={calendarBodyRef} className={styles.calendarBody}> {months.map(month => { return <div>[...your logic here]<div> })} </div> )
Hope that helps.