Haroon Riaz
Haroon Riaz

Reputation: 13

React-Big-Calendar - week view scrolls to Top on every render

I am using the DND calendar of react-big-calendar. The problem I am facing is that on every render it scrolls the view back to top, whenever a state changes or a call is made to an API, it renders the Calendar and the view takes me back to the top

import React, { useState, useEffect, useMemo } from "react";
import { Calendar, momentLocalizer } from "react-big-calendar";
import "react-big-calendar/lib/css/react-big-calendar.css";
import moment from "moment";
import { Box, Text } from "@chakra-ui/react";
import CustomToolbar from "./CustomToolbar";
import withDragAndDrop from "react-big-calendar/lib/addons/dragAndDrop";
import "react-big-calendar/lib/addons/dragAndDrop/styles.css";
import axios from "axios";
import firebase, { auth } from "../../firebase/firebase";
import { useAuthState } from "react-firebase-hooks/auth";
import { Droppable } from "react-beautiful-dnd";
import { fetchEvents, getAccessToken } from "../../helper/CalendarHelpers";
import "moment/locale/en-gb";
import {
  ContextMenu,
  ContextMenuItem,
  ContextMenuList,
  ContextMenuTrigger,
} from "@saas-ui/react";

const server = process.env.REACT_APP_BACKEND_URL;

const localizer = momentLocalizer(moment);

const MyCalendar = ({ setEvents, events }) => {
  const DnDCalendar = withDragAndDrop(Calendar);

  const [user] = useAuthState(auth);
  const [date, setDate] = useState(moment());
  const [startDay, setStartDay] = useState("Sunday");
  const [loading, setLoading] = useState(false);
  const [scrollPosition, setScrollPosition] = useState(0);
  const [scrolltime, setscrolltime] = useState(new Date(0, 0, 0, 7));

  useEffect(() => {
    fetchEvents(user, setEvents);
  }, []);

  const configureStartDay = (startDay) => {
    const dayIndex = ["su", "mo", "tu", "we", "th", "fr", "sa"].indexOf(
      startDay.slice(0, 2).toLowerCase()
    );

    if (dayIndex === -1) {
      throw new Error("Invalid start day");
    }

    moment.updateLocale("en", {
      week: {
        dow: dayIndex,
      },
    });

    return "en";
  };

  useEffect(() => {
    firebase
      .firestore()
      .collection("calendar")
      .doc(user.uid)
      .get()
      .then((snap) => {
        if (snap.exists) {
          if (snap?.data()?.startingDay) {
            setStartDay(snap?.data()?.startingDay);
          }
        }
      })
      .catch((error) => {
        console.log("Error in Calendar.js===>", error);
      });
  }, [user.uid]);

  const memoizedEvents = useMemo(() => {
    const CustomHeader = ({ date }) => {
      const formattedDate = moment(date).format("ddd DD");
      return (
        <Box
          style={{
            userSelect: "none",
            display: "flex",
            flex: "1",
            flexDirection: "column",
          }}
        >
          <Text
            style={{
              fontSize: "1rem",
              fontWeight: "normal",
              color: "#777272",
            }}
          >
            {formattedDate.split(" ")[0]}
          </Text>
          <Text
            style={{
              fontSize: "1rem",
              fontWeight: "bold",
            }}
          >
            {formattedDate.split(" ")[1]}
          </Text>
        </Box>
      );
    };

    return CustomHeader;
  }, []);

  const handleDateSelect = (date) => {
    setDate(moment(date.toDate()));
  };

  const handleNavigate = (navigate, view) => {
    if (navigate === "prev") {
      setDate(date.clone().subtract(1, view));
    } else if (navigate === "next") {
      setDate(date.clone().add(1, view));
    }
  };

  const updateEvent = async (data) => {
    const { start, end, event, isAllDay } = data;
    if (isAllDay) {
      if (start.getDate() === end.getDate()) {
        start.setHours(0, 0, 0, 0);
        end.setDate(start.getDate() + 1);
        end.setHours(0, 0, 0, 0);
      }
    }

    const updatedEvent = { ...event, start, end };
    let neweve = events.filter((x) => x.id !== updatedEvent.id);
    setEvents([...neweve, updatedEvent]);
    setscrolltime(start);
    try {
      const userId = user.uid;
      const calendarId = event.calendarId;
      let accessToken;
      let type;
      const calendarDoc = await firebase
        .firestore()
        .collection("calendar")
        .doc(userId)
        .get();
      if (calendarId) {
        type = "refresh";
        accessToken = getAccessToken(
          calendarId,
          calendarDoc.data().AllCalendar
        );
      }

      await axios.put(`${server}/google/calendar/update-event/${event.id}`, {
        calendarId: calendarId,
        updatedEvent: updatedEvent,
        accessToken: accessToken,
        type: type,
      });
    } catch (error) {
      console.error("Error updating event:", error);
    }
  };

  const handleDelete = async (event) => {
    try {
      const userId = user.uid;
      const calendarId = event.calendarId;
      let accessToken;
      console.log("CALENDAR ID===>", calendarId);
      let type;
      const calendarDoc = await firebase
        .firestore()
        .collection("calendar")
        .doc(userId)
        .get();
      if (calendarId) {
        type = "refresh";
        accessToken = getAccessToken(
          calendarId,
          calendarDoc.data().AllCalendar
        );
      }

      const filteredEvents = events.filter((e) => e.id !== event.id);
      setEvents(filteredEvents);
      await axios.delete(`${server}/google/calendar/delete-event/${event.id}`, {
        data: {
          calendarId: calendarId,
          accessToken: accessToken,
        },
      });
    } catch (error) {
      console.log("Error in Delete", error);
    }
  };

  const onEventDrop = async (data) => {
    await updateEvent(data);
  };

  const onEventResize = async (data) => {
    await updateEvent(data);
  };

  const eventStyleGetter = () => {
    const style = {
      backgroundColor: "#EAF6FF",
      color: "#1C67D2",
    };
    return { style };
  };

  const customTimeFormat = (date) => moment(date).format("h A");
  const customEvents = ({ event, children }) => {
    return (
      <ContextMenu>
        <ContextMenuTrigger longPressDisabled={true}>
          <div>{children}</div>
        </ContextMenuTrigger>
        <ContextMenuList pos={"relative"} bg={"#33373d"} minW={0} pt={0} pb={0}>
          <ContextMenuItem
            style={{
              backgroundColor: "#33373d",
              color: "white",
              fontSize: 14,
            }}
            borderRadius={"0.375rem"}
            onClick={() => {
              handleDelete(event);
            }}
          >
            Delete
          </ContextMenuItem>
        </ContextMenuList>
      </ContextMenu>
    );
  };

  const CustomTimeSlot = ({ children, value }) => {
    return (
      <Droppable droppableId={"Calendar-" + value}>
        {(provided) => (
          <Box
            ref={provided.innerRef}
            {...provided.droppableProps}
            style={{ display: "flex", alignItems: "center" }}
          >
            {children}
            {provided.placeholder}
          </Box>
        )}
      </Droppable>
    );
  };

  const CustomWeek = (props) => {
    return <Box bg={"red"}>{console.log(props)}</Box>;
  };

  return (
    <Box mx={6} style={{ height: "94vh", width: "100vw" }}>
      <style>
        {`
          .rbc-addons-dnd-row-body .rbc-row:nth-of-type(2) {
            display: none !important;
          }
          .rbc-event-allday {
            border: 2px solid #756ece !important;
            background-color : #dfe1f6 !important;
          }
          .rbc-event {
            border: 2px solid #756ece !important;
            background-color : #dfe1f6 !important;
          }
          .rbc-label{
            font-size: 12px !important;
            color:#777272;
          }
          .rbc-time-header-content{
            margin-left:45px;
          }
          .rbc-label rbc-time-header-gutter {
            display: none !important;
          }

          div.rbc-header.rbc-today p.chakra-text.css-0{
            color: #3761EE !important;

          }
          div.rbc-header.rbc-today {
            border-bottom: 2px solid #3761EE;
          }
          div.rbc-header.rbc-today {
           
            background-color: white;
        }
        .rbc-day-slot .rbc-event-content {
          display: flex !important;
          justify-content: center !important;
          align-items: center !important;
          text-align: center !important;
          font-weight: 550 !important;
          color: #756ece !important;}
          
          .rbc-day-slot .rbc-event{
            text-align:center;
            font-weight:550;
            border: 2px solid #756ece !important;
            background-color : #dfe1f6 !important;
          }
          .rbc-day-slot .rbc-events-container {
          margin-right:0px !important;
          }
        div.rbc-day-slot.rbc-time-column.rbc-now.rbc-today{
          background-color:white;
        }
        .rbc-day-slot .rbc-event-label {
          display:none;
         }
          .rbc-timeslot-group {
            min-height:70px;
          }
        
          .rbc-header + .rbc-header {
            border-left: 1px solid #ddd;
            border-top: 1px solid #FFFFFF;
            border-bottom: 0px;
            
        }
        .rbc-time-view{
          border:none;
          width: auto;
        }
        .rbc-time-content{
          border-top:1px solid #ddd
          overflow-y:scroll;
        }
        .rbc-time-content::-webkit-scrollbar {
          display: none;
      }
      div.rbc-time-header.rbc-overflowing {
        margin-right: -2px !important;
      }
        .rbc-header{
          border-bottom:0;
          text-align: start;
          padding-left: 20px;
        }
        .rbc-current-time-indicator {
          background-color: #3761EE !important;
        }
        .rbc-time-gutter .rbc-timeslot-group {
          display: block !important;
          width: 50px !important;
        }
        .rbc-time-header{
          display:block !important
        }
        `}
      </style>

      <DnDCalendar
        key={"12345"}
        allDayAccessor="isAllDay"
        showAllEvents={true}
        showMultiDayTimes={true}
        localizer={localizer}
        events={events}
        startAccessor="start"
        endAccessor="end"
        defaultView="week"
        onEventDrop={onEventDrop}
        onEventResize={onEventResize}
        resizable
        style={{
          height: "100%",
          width: "100%",
        }}
        components={{
          toolbar: (props) => (
            <CustomToolbar
              {...props}
              onNavigate={handleNavigate}
              date={date}
              onDateChange={handleDateSelect}
            />
          ),
          header: memoizedEvents,
          eventWrapper: customEvents,
          timeSlotWrapper: CustomTimeSlot,
          week: CustomWeek,
        }}
        date={date.toDate()}
        eventPropGetter={eventStyleGetter}
        formats={{
          timeGutterFormat: (date) => customTimeFormat(date),
        }}
        culture={configureStartDay(startDay)}
      />
    </Box>
  );
};

export default MyCalendar;

I have tried the enableAutoScroll prop as well but to no luck, I have also tried numerous things but couldn't understand the problem behind it. I would really appreciate the help.

Thanks a lot.

Upvotes: 1

Views: 438

Answers (2)

Rj Patel
Rj Patel

Reputation: 1

Just declare const DnDCalendar = withDragAndDrop(Calendar) outside the MyCalendar component

and your problem will be solved

Upvotes: 0

Hamude Shahin
Hamude Shahin

Reputation: 321

Try this way:

const [shouldScroll, setShouldScroll] = useState(false);

// ... other code

useEffect(() => {
  setShouldScroll(true); // Set flag on initial load (optional)
  fetchEvents(user, setEvents);
}, [user]);

// ... other code

return (
  <BigCalendar
    // ... other props
    scrollToTime={shouldScroll ? scrolltime : null} // Pass scrollTime only when flag is true
    // ... other props
  />
);

Upvotes: 0

Related Questions