You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Syncfusion:如何给日程编辑器窗口添加MultiSelectComponent字段

Syncfusion MultiSelectComponent集成Schedule编辑器窗口时触发TypeError: Cannot read properties of undefined (reading 'enqueueForceUpdate')

错误栈

Uncaught TypeError: Cannot read properties of undefined (reading 'enqueueForceUpdate')
    at Component.forceUpdate (react.development.js:368:16)
    at ComponentBase.renderReactTemplates (component-base.js:473:18)
    at Observer.eval (multi-select.js:281:23)
    at Observer.notify (observer.js:92:32)
    at ComponentBase.trigger (component-base.js:266:36)
    at MultiSelect.onPopupShown (multi-select.js:268:14)
    at Observer.eval (multi-select.js:5122:23)
    at Observer.notify (observer.js:92:32)
    at ComponentBase.trigger (component-base.js:266:36)
    at MultiSelect.showPopup (multi-select.js:5102:14)
    at MultiSelect.wrapperClick (multi-select.js:1099:22)

组件代码

"use client";

import * as React from "react";
import {
  ScheduleComponent,
  ViewsDirective,
  ViewDirective,
  Week,
  Month,
  Inject,
} from "@syncfusion/ej2-react-schedule";
import { MultiSelectComponent } from "@syncfusion/ej2-react-dropdowns";
import { createElement } from "@syncfusion/ej2-base";

const Calendar = () => {
  const [eventData, setEventData] = React.useState([]);

  const onPopupOpen = (args) => {
    if (args.type === "Editor") {
      if (!args.element.querySelector(".custom-field-row")) {
        let row = createElement("div", { className: "custom-field-row" });
        let formElement = args.element.querySelector(".e-schedule-form");
        formElement.firstChild.insertBefore(
          row,
          formElement.firstChild.firstChild
        );

        let container = createElement("div", {
          className: "custom-field-container",
        });
        let inputEle = createElement("input", {
          className: "e-field",
          attrs: { name: "Members" },
        });
        container.appendChild(inputEle);
        row.appendChild(container);

        new MultiSelectComponent({
          dataSource: [
            {
              id: 1,
              text: "John Doe",
              email: "john.doe@example.com",
            },
            {
              id: 2,
              text: "Jane Smith",
              email: "jane.smith@example.com",
            },
            {
              id: 3,
              text: "Alice Johnson",
              email: "alice.johnson@example.com",
            },
          ],
          fields: { text: "text", value: "id" },
          placeholder: "Select Members",
          mode: "Box",
          showDropDownIcon: true,
          allowFiltering: true,
        }).appendTo(inputEle);
      }
    }
  };

  const onActionBegin = (args) => {
    if (args.requestType === "eventCreate") {
      console.log("New appointment created:", args.data);
      setEventData([...eventData, ...args.data]);
    }
    if (args.requestType === "eventChange") {
      console.log("Appointment updated:", args.data);
      setEventData(
        eventData.map((event) =>
          event.Id === args.data.Id ? args.data : event
        )
      );
    }
    if (args.requestType === "eventRemove") {
      console.log("Appointment removed:", args.data);
      setEventData(eventData.filter((event) => event.Id !== args.data[0].Id));
    }
  };

  return (
    <div className="mt-10">
      <ScheduleComponent
        height="800px"
        eventSettings={{
          dataSource: eventData,
          allowMultiDrag: false,
        }}
        actionBegin={onActionBegin}
        popupOpen={onPopupOpen}
        allowMultiRowSelection={false}
        allowMultiCellSelection={false}
      >
        <ViewsDirective>
          <ViewDirective option="Week" />
          <ViewDirective option="Month" />
        </ViewsDirective>
        <Inject services={[Week, Month]} />
      </ScheduleComponent>
    </div>
  );
};

export default Calendar;

已尝试的方案

  • 确保MultiSelectComponent在onPopupOpen函数内初始化
  • 检查依赖版本一致性
  • 尝试用useRef方式初始化MultiSelectComponent

关键观察

使用普通DropdownListComponent时无此问题,仅MultiSelect出现该错误。


解决思路及修复方案

错误根因

当前用new MultiSelectComponent().appendTo(inputEle)的方式是Syncfusion原生JS组件的初始化逻辑,但MultiSelectComponent是React组件,这种方式会让它脱离React的组件上下文,导致组件内部调用forceUpdate时找不到React的enqueueForceUpdate方法——React组件必须挂载在React管理的组件树中,才能获取到更新队列。

修复方法

改用React的方式渲染MultiSelect组件,通过ReactDOM.render将其挂载到创建的DOM容器中,确保它处于React上下文:

  1. 导入ReactDOM:
import * as ReactDOM from 'react-dom/client';
  1. 修改onPopupOpen中的MultiSelect初始化逻辑:
const onPopupOpen = (args) => {
  if (args.type === "Editor") {
    if (!args.element.querySelector(".custom-field-row")) {
      let row = createElement("div", { className: "custom-field-row" });
      let formElement = args.element.querySelector(".e-schedule-form");
      formElement.firstChild.insertBefore(row, formElement.firstChild.firstChild);

      // 创建React根容器,而非普通input
      let container = createElement("div", { className: "custom-field-container" });
      row.appendChild(container);

      // 使用ReactDOM渲染MultiSelect组件
      const root = ReactDOM.createRoot(container);
      root.render(
        <MultiSelectComponent
          dataSource={[
            { id: 1, text: "John Doe", email: "john.doe@example.com" },
            { id: 2, text: "Jane Smith", email: "jane.smith@example.com" },
            { id: 3, text: "Alice Johnson", email: "alice.johnson@example.com" },
          ]}
          fields={{ text: "text", value: "id" }}
          placeholder="Select Members"
          mode="Box"
          showDropDownIcon={true}
          allowFiltering={true}
          name="Members" // 设置name属性对应表单字段
        />
      );
    }
  }
};
  1. 额外优化:清理React根避免内存泄漏
    监听Schedule的popupClose事件,销毁创建的React根:
const onPopupClose = (args) => {
  if (args.type === "Editor") {
    const container = args.element.querySelector(".custom-field-container");
    if (container) {
      const root = ReactDOM.createRoot(container);
      root.unmount();
      container.remove();
    }
  }
};

然后在ScheduleComponent中添加popupClose={onPopupClose}属性。

为什么DropdownList没问题?

DropdownList的原生JS版本和React版本的边界处理逻辑不同,它在非React上下文初始化时未触发需要React更新队列的操作;而MultiSelect的Box模式等特性依赖React的模板渲染,必须处于React上下文才能正常工作。


内容的提问来源于stack exchange,提问作者FD3

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 11:00:15