Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How can I prevent my child components re-rendering?

I’m creating a calendar date range picker in react js and I’m running into an issue where, whenever I click a day in the calendar, all of the days end up re-rendering.

Here is my main calendar component

import React, { useCallback, useEffect, useMemo, useState } from 'react';
import CalendarDay from './CalendarDay';

export default function Calendar(props) {
  const days = [
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
    22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
  ];

  const [selectedDates, setSelectedDates] = useState({
    start: null,
    end: null,
  });
  useEffect(() => {
    console.log(selectedDates);
  }, [selectedDates]);

  const handleDateSelect = useCallback(
    (day) => {
      if (!selectedDates.start) {
        setSelectedDates((prev) => ({ ...prev, start: day }));
      } else if (!selectedDates.end) {
        setSelectedDates((prev) => ({ ...prev, end: day }));
      } else {
        setSelectedDates({ start: day, end: null });
      }
    },
    [selectedDates.start, selectedDates.end]
  );

  return (
    <>
      <button onClick={() => setSelectedDates({ start: null, end: null })}>
        clear dates
      </button>
      <div>start: {selectedDates.start}</div>
      <div>end: {selectedDates.end}</div>

      <div className="calendar">
        {days.map((day, idx) => {
          const key = `${props.year}-${props.month}-${idx + 1}`;
          const isSelectedOrInRange = () => {
            if (
              selectedDates.start === day ||
              selectedDates.end === day ||
              (day <= selectedDates.end && day >= selectedDates.start)
            ) {
              return true;
            }
            return false;
          };
          return (
            <CalendarDay
              key={key}
              day={day}
              isSelectedOrInRange={isSelectedOrInRange()}
              handleDateSelect={handleDateSelect}
            />
          );
        })}
      </div>
    </>
  );
}

And here is my CalendarDay component (this is whats always re-rendering)

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

import React, { memo } from 'react';

function CalendarDay(props) {
  const { handleDateSelect, day, isSelectedOrInRange } = props;

  console.log(' - rendering: ', day);

  return (
    <div
      className={'day' + (isSelectedOrInRange ? ' selected' : '')}
      onClick={() => handleDateSelect(day)}
    >
      {day}
    </div>
  );
}

export default memo(CalendarDay);

styles.css (imported in index.js)

* {
  box-sizing: border-box;
}
.calendar {
  width: 400px;
  display: flex;
  flex-wrap: wrap;
}
.day {
  width: calc(400px / 7);
  height: 50px;
  border: 1px solid green;
  text-align: center;
  display: flex;
  justify-content: center;
  align-items: center;
}
.selected {
  color: white;
  background: red;
}

Is there a way to prevent any calendarDay box from re-rendering unless its clicked on or a date in between the selection?

Here is a stackblitz for you to play around with

>Solution :

Since you’re using React.memo, the CalendarDay only renders when its props change, but you’re changing the handleDateSelect function anytime selectedDates.start or selectedDate.end changes.

One way to avoid this is to only use the prev value in the handleDateSelect function, avoiding the need to declare any dependencies for the callback:

 const handleDateSelect = useCallback(
    (day) => {
      setSelectedDates((prev) => 
        !prev.start
          ? { ...prev, start: day }
          : !prev.end
          ? { ...prev, end: day }
          : { start: day, end: null }
    },
    []
  );
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading