Ad

How To Set Initial State With Undo Action On React?

I have a react-big-calendar with drag and drop, I want when I move an event to another position, a snackbar appeared which having the undo action like that :

enter image description here

I want when I click on the undo action, the event set the initial state (the initial position)

My code is :

 moveEvent = ({event, start, end, isAllDay: droppedOnAllDaySlot}) => {
      const { events } = this.state;
      const idx = events.indexOf(event);
      let allDay = event.allDay ;
      if(!event.allDay && droppedOnAllDaySlot)
      {allDay = true;}
      else if (event.allDay && !droppedOnAllDaySlot)
      { allDay = false;}
      const updatedEvent = { ...event, start, end, allDay }
      const nextEvents = [...events]
      nextEvents.splice(idx, 1, updatedEvent)
      this.setState({
        events : nextEvents,
        dragAndDrop : true,
        open : true
      })
  }
  handleClose = () => {
      this.setState({ open: false });
  };
  handleClick = () => {
      this.setState({ open: true });
  };
  handleUndo = () => {
      this.setState({
        dragAndDrop : !this.state.dragAndDrop,
        events: this.state.events
      })
 }
  render() {
    return (
       <div>
          <DragAndDropCalendar
            selectable
            localizer={localizer}
            events={this.state.events} 
            views={['month','week','day']}
            //defaultDate={new Date(2019, 2, 19)}
            defaultView="week"
            culture = 'fr'
            timeslots={1}
            step={15}
            style={{ height: "100vh" }}
            onEventDrop={this.moveEvent}
            min={new Date(2017, 10, 0, 7, 0, 0)}
            max={new Date(2017, 10, 0, 21, 0, 0)} 
            resizable  
            onEventResize={this.resizeEvent}
            onSelectSlot={this.newEvent}
            onSelectEvent={this.handleClickOpen}
          />
        <Snackbar
          anchorOrigin={{
            vertical: 'bottom',
            horizontal: 'center',
          }}
          open={this.state.open}
          autoHideDuration={6000}
          onClose={this.handleClose}
          ContentProps={{
            'aria-describedby': 'message-id',
          }}
          message={<span id="message-id">Evénement enregistré</span>}
          action={[
              <Button key="undo" color="secondary" size="small" onClick={this.handleUndo}>
                Annuler
              </Button>,
              <IconButton
                key="close"
                aria-label="Close"
                color="inherit"
                onClick={this.handleClose}
              >
                <CloseIcon />
              </IconButton>,
            ]}
        />

My full code : https://codesandbox.io/s/mq42x1j90x

When I run my code and I click on the undo action, it doesn't work, and my event doesn't set the initial position.

How can I fix that ?

Ad

Answer

I suggest to store previous events in separate field inside this.state(for instance, this.state.previousEvents), and at undo() action set current events to previous events. Also, when user closes snackbar or do some other action which changes events and can't be reverted, don't forget to update previousEvents as current events value.

Also instead of using this.state in this.setState

this.setState({
        dragAndDrop : !this.state.dragAndDrop,
        events: this.state.events
      })

you should use previousState:

this.setState((prevState) => ({
        dragAndDrop : !prevState.dragAndDrop,
        events: prevState.events
      }))

So the full version is

class Calendar extends Component {
  state  = {
    events: [],
    prevEvents: []
  };

  handleUndo = () => {
    this.setState((prevState) => ({
      events: prevState.prevEvents
    }))
  }

  handleCloseSnackbar = () => {
    this.setState((prevState) => ({
      prevEvents: prevState.events
    }))
  }

  ...
}
Ad
source: stackoverflow.com
Ad