如何在 react-big-calendar 中显示动态事件?

How to display dynamic events in react-big-calendar?

我已经使用 react-big-calendar 来显示动态事件,我的 api url 就像“api/user/events/year/month/”,我必须在其中提供当前观看年份和月份获取事件的编号。我已经使用 onNavigate 和 onView 来更新当前日期和日历的当前视图。我得到了正确的可见范围,但在提供月数和年份时,没有传递正确的月数。

显示事件的日历组件:

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>
  );
}

  

我想你的减速器和你当地的状态之间存在竞争条件。首先,尽可能使用 moment。其次,使用函数局部变量以相同的值更新 reducer 中的状态和本地状态。只是一个猜测。您可能还想将此 computeDisplayedDateRange() 包装在 useCallback 中,以便 知道 您正在使用当前的 currentDatecurrentView 值。

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
  }));
}