0

I have used react-big-calendar to display the dynamic events, my api url is like "api/user/events/year/month/" where I have to provide current viewing year and month number to fetch the events. I have used onNavigate and onView to update current date and current view of calendar. I am getting correct visible range but while providing month number and year, correct month number is not getting passed.

Calendar component to display events:

import React, { useEffect, useState } from 'react'
import { Calendar, momentLocalizer } from 'react-big-calendar'
import moment from "moment";
import "react-big-calendar/lib/css/react-big-calendar.css"
import * as dates from "../../utils/EMDates"
import { Card, CardContent, Paper, Typography, SvgIcon, CardActions, Button } from '@material-ui/core';
import EMCustomToolbar from "./EMCustomToolbar" 
import { useSelector, useDispatch } from "react-redux";
import IconButton from '@material-ui/core/IconButton';
import CardHeader from '@material-ui/core/CardHeader';
import Dialog from '@material-ui/core/Dialog';
import { SHOW_EVENT_DETAILS } from '../../redux/constants/UserPlatform/EMEventsConstant';
import { useNavigate } from 'react-router';
import { EMDoGetMonthEventsAction } from '../../redux/actions/UserPlatform/Events';

const ColoredDateCellWrapper = ({ children }) =>
  React.cloneElement(React.Children.only(children), {
    style: {
      backgroundColor: 'lightblue',
    },
  })

  function  eventStyleGetter (event) {
    var backgroundColor = '#' + event.hexColor;
    var style = {
        backgroundColor: backgroundColor,
        borderRadius: '12px',
        color: 'black',
        border: '0px',
        display: 'block'
    };

    if (event.isMine){
      style.backgroundColor = "lightgreen"
    }
    return {
        style: style
    };
  }

const localizer = momentLocalizer(moment);

function EMCalendar({data, ...props}) {
  const [open, setOpen] = useState(false);
  const [selectedevent, setSelectedEvent] = useState({})
  const dispatch = useDispatch()
  const navigate = useNavigate()
  const [currentDate, setCurrentDate] = useState(new Date())
  const [currentView, setCurrentView] = useState("month")
  const userData = useSelector((state)=> state.auth || {})
  const [calendarView, setCalendarView] = React.useState({})
  const [displayedDateRange, setdisplayedDateRange] = React.useState({})


  function tConvert (time) {
    // Check correct time format and split into components
    time = time.toString ().match (/^([01]\d|2[0-3])(:)([0-5]\d)(:[0-5]\d)?$/) || [time];
  
    if (time.length > 1) {
      time = time.slice (1); 
      time[5] = +time[0] < 12 ? 'AM' : 'PM';
      time[0] = +time[0] % 12 || 12; 
    }
    return time.join ('');
  }

let setCurrentDate1 = async (date) => {
  await computeDisplayedDateRange(); 
  setCurrentDate(date);
}
let setCurrentView1 = async (view) => {
  await computeDisplayedDateRange();
   setCurrentView(view)

}

let computeDisplayedDateRange = () => {
  setCalendarView({year: currentDate.getFullYear(),
    month: currentDate.getMonth()})
  let start = moment(currentDate).startOf(currentView);
  let end = moment(currentDate).endOf(currentView);
if(currentView == 'month') {
  start = start.startOf('week');
  end = end.endOf('week');
}
setdisplayedDateRange({start:start.toString(), end:end.toString()})
dispatch(EMDoGetMonthEventsAction({userData, calendarView:calendarView}))
}

useEffect(() => {
  computeDisplayedDateRange()
}, [currentDate, currentView])

const monthNames = ["JAN", "FEB", "MAR", "APR", "MAY", "JUN",
  "JUL", "AUG", "SEPT", "OCT", "NOV", "DEC"
];


  return(
    <div>
      <div>Displayed Date Rage: {displayedDateRange.start} - {displayedDateRange.end}</div>
    <Paper elevation={4}>
    
  <Calendar
onNavigate={setCurrentDate1}
onView={setCurrentView1}
  localizer={localizer}
    events={data}
    // views={views}
    style={{ height: 750 }}
    showMultiDayTimes
    max={dates.add(dates.endOf(new Date(), 'day'), -1, 'hours')}
    // defaultDate={new Date()}
    date={currentDate}
    view={currentView}
    startAccessor="start"
            endAccessor="end"
    components={{
      timeSlotWrapper: ColoredDateCellWrapper,
      toolbar : EMCustomToolbar
    }}
    eventPropGetter={eventStyleGetter}
    onSelectEvent = {(event) => {
      setOpen(true);
      setSelectedEvent(event)
    }}
  />
  </Paper>
  <Dialog
      open={open}
      onClose={() => {setOpen(false)}}
      aria-labelledby="alert-dialog-title"
      aria-describedby="alert-dialog-description"
      maxWidth="xs"
    >
      <div style={{position:"absolute", top:0, right:0}}>
            <IconButton onClick={() => {setOpen(false)}} color="secondary">
              <DeleteIcon />
            </IconButton>
          </div>
   <Card>
     <div style={{display:"flex"}}>
   <CardHeader
   style={{maxWidth:"70%"}}
        title={selectedevent && selectedevent.title}
        subheader={<>
          <Typography>{selectedevent.event_start_time && tConvert(selectedevent.event_start_time)} {" "}-{" "} 
          {selectedevent.event_end_time && tConvert(selectedevent.event_end_time)}
         </Typography>
        <Typography color="textPrimary" style={{fontWeight:"bolder",}}>{selectedevent && selectedevent.event_location}</Typography>
      
        </>}
        />
        <div style={{margin:"auto", textAlign:"center"}}>
        <Typography component="h6" variant="h6" style={{fontWeight:"bolder"}}>
            {selectedevent.start && selectedevent.start.getDate()}
            </Typography>
        <Typography component="h6" variant="h6" style={{fontWeight:"bolder"}}>{selectedevent.start && monthNames[selectedevent.start.getMonth()]}</Typography>
        </div>
        </div>
        <CardContent>
          <Typography>
            {selectedevent && selectedevent.postText}
          </Typography>
        </CardContent>
   </Card>
          <CardActions style={{justifyContent: 'center', marginBottom:'2%'}}>
            <Button variant="contained" style={{backgroundColor: "#3399ff"}}
            onClick={() => {
              dispatch({
                type:SHOW_EVENT_DETAILS,
                payload:{
                  route:selectedevent.route,
                  eventId:selectedevent.id,
                  value:true
                },
              })
              if(selectedevent.route === "eventsByMe" || "allMyEvents"){
                navigate("/users/events/yourevents")
              } else {
                navigate("/users/events/searchevents")
              }
            }}>
              View Details
            </Button>
          </CardActions>
    </Dialog>
  </div>
  )
}

export default EMCalendar;

function DeleteIcon(props) {
  return (
    <SvgIcon  {...props}>
      <path d="M11.436,3.375A8.061,8.061,0,1,0,19.5,11.436,8.06,8.06,0,0,0,11.436,3.375Zm2.042,10.979-2.042-2.042L9.394,14.354a.619.619,0,1,1-.876-.876l2.042-2.042L8.518,9.394a.619.619,0,0,1,.876-.876l2.042,2.042,2.042-2.042a.619.619,0,1,1,.876.876l-2.042,2.042,2.042,2.042a.622.622,0,0,1,0,.876A.615.615,0,0,1,13.478,14.354Z" />
    </SvgIcon>
  );
}

  
Suraj Pawar
  • 227
  • 1
  • 4
  • 15

1 Answers1

1

I imagine there's a race condition between your reducer and your local state. First, use moment where you can. Second, use function local variables to update state in your reducer and local state with the same values. Just a guess. You may also want to wrap this computeDisplayedDateRange() in a useCallback to know that your working with the current currentDate and currentView values.

let computeDisplayedDateRange = () => {
  const thisDate = moment(currentDate)
  const newCalView = {
    year: thisDate.year(),
    month: thisDate.month()
  };
  setCalendarView(newCalView);
  // wrap `thisDate` in a new moment, as moment is mutable
  const start = moment(thisDate).startOf(currentView);
  const end = moment(thisDate).endOf(currentView);
  if(currentView == 'month') {
    start = start.startOf('week');
    end = end.endOf('week');
  }
  setdisplayedDateRange({
    start: start.toDate().toString(), 
    end: end.toDate().toString()
  });
  dispatch(EMDoGetMonthEventsAction({
    userData, 
    calendarView: newCalView
  }));
}
Steve -Cutter- Blades
  • 5,057
  • 2
  • 26
  • 40
  • Actually problem was, api call was getting fired after first render. By the time api call is given calendar wasn't rendered. Hence, i was not getting the current month and year. So I had to make sure that calendar is first rendered and then had make api call. So I simply used asynchronous behaviour. But your solution also looks promosing, I will try that too. – Suraj Pawar Sep 29 '21 at 04:57