Skip to content
Advertisement

How to stop my component from being made twice in development

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

enter image description here

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.

User contributions licensed under: CC BY-SA
10 People found this is helpful
Advertisement