【问题标题】:How to set initial state with undo action on React?如何在 React 上使用撤消操作设置初始状态?
【发布时间】:2025-12-27 08:45:11
【问题描述】:

我有一个带有拖放功能的反应大日历,我希望当我将事件移动到另一个位置时,会出现一个具有类似撤消操作的小吃栏:

我希望当我点击撤消动作时,事件设置初始状态(初始位置)

我的代码是:

 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>,
            ]}
        />

我的完整代码:https://codesandbox.io/s/mq42x1j90x

当我运行我的代码并单击撤消操作时,它不起作用,并且我的事件没有设置初始位置。

我该如何解决这个问题?

【问题讨论】:

  • 如果您的意思是您在handleUndo() 中所做的事情,那么events: this.state.events 没有做任何事情,因为您只是为自己分配了相同的值。除了初始状态的价值是什么?它不在您提供的代码中。
  • @MarsonMao,是的,我想取消handleUndo()上的拖放操作
  • 也取消events吧?
  • 正是@MarsonMao
  • 所以也许你只是做events: []

标签: javascript reactjs drag-and-drop setstate react-big-calendar


【解决方案1】:

我建议将以前的事件存储在 this.state 中的单独字段中(例如,this.state.previousEvents),并在 undo() 操作中设置当前事件到以前的事件。此外,当用户关闭snackbar 或执行一些其他更改事件且无法恢复的操作时,不要忘记将previousEvents 更新为当前事件值。

this.setState 中也不要使用 this.state

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

你应该使用previousState

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

所以完整版是

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

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

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

  ...
}

【讨论】:

  • 我把Uncaught TypeError: Cannot read property 'setState' of undefined 变成handleUndo()
  • @CodeLover 这很奇怪,但这只是一个简单的例子,我可能打错了一些东西。我们可以在聊天中讨论,也可以将代码粘贴到沙盒中
  • 我编辑了你的沙箱,它可以工作,请检查它codesandbox.io/s/kkm0okkr4o
  • 什么时候从数据库中获取事件,并在我的状态下初始化它:events :[]
  • 您可以创建一个 HOC(High-order-component),它将连接到存储并从中获取事件。并将事件作为道具传递给您的日历。然后,在日历中,您可以编写构造函数并根据传递的道具初始化您的状态。这是通用代码的链接(它不起作用,它只是你可以做什么的想法)pastebin.com/KMNVXHCJ