SharePoint / sp-dev-docs

SharePoint & Viva Connections Developer Documentation
https://docs.microsoft.com/en-us/sharepoint/dev/
Creative Commons Attribution 4.0 International
1.25k stars 1.02k forks source link

SPFx Command Set - React 17 / ReactDOM - Intermittent #321 Error #9984

Open chrisredman01 opened 4 weeks ago

chrisredman01 commented 4 weeks ago

Target SharePoint environment

SharePoint Online

What SharePoint development model, framework, SDK or API is this about?

💥 SharePoint Framework

Developer environment

Windows

What browser(s) / client(s) have you tested

Additional environment details

Describe the bug / error

We are experiencing an issue similar to #9640, in which we have a Command Set button that renders a Panel component using ReactDOM.render(...) when clicked. Occasionally this errors and the console has the #321 React error.

The issue is not specific to the Panel component. Fully removing the Fluent UI React package and rendering a component that outputs a plain div which also uses hooks e.g. useEffect will get the same intermittent error.

image

After a page refresh, it works again.

The intermittent nature makes this difficult to fully confirm, but these are the current findings:

Example code:

Command Set configured to attached to Lists and Libraries

import {
  BaseListViewCommandSet,
  type Command,
  type IListViewCommandSetExecuteEventParameters,
} from "@microsoft/sp-listview-extensibility";
import * as React from "react";
import * as ReactDOM from "react-dom";
import CommandPanel from "./components/CommandPanel";

export default class ArchiveCommandSet extends BaseListViewCommandSet<IArchiveCommandSetProperties> {
  private _panelPlaceHolder: HTMLDivElement;

  public onInit(): Promise<void> {
    this._panelPlaceHolder = document.body.appendChild(
      document.createElement("div")
    );

    const archiveCommand: Command = this.tryGetCommand("COMMAND_ARCHIVE");
    archiveCommand.visible = true;

    return Promise.resolve();
  }

  public onExecute(event: IListViewCommandSetExecuteEventParameters): void {
    switch (event.itemId) {
      case "COMMAND_ARCHIVE":
        this._renderPanelComponent();
        break;
      default:
        throw new Error("Unknown command");
    }
  }

  public onDispose(): void {
    ReactDOM.unmountComponentAtNode(this._panelPlaceHolder);
  }

  private _renderPanelComponent = (): void => {
    const element: React.ReactElement = React.createElement(CommandPanel);
    ReactDOM.render(element, this._panelPlaceHolder);
  };
}

Failing Component:

import * as React from "react";

const CommandPanel: React.FunctionComponent = () => {
  React.useEffect(() => { console.log("hello"); }, []);

  return (
    <div
      style={{
        position: "absolute",
        top: "20%",
        left: "40%",
        backgroundColor: "red",
        fontSize: "18px",
        padding: "2em 1em",
        zIndex: 99999,
      }}
    >
      hello world!
    </div>
  );
};

export default CommandPanel;

package.json

{
  "name": "test",
  "version": "0.0.1",
  "private": true,
  "engines": {
    "node": ">=18.17.1 <19.0.0"
  },
  "main": "lib/index.js",
  "scripts": {
    "build": "gulp bundle",
    "clean": "gulp clean",
    "test": "gulp test"
  },
  "dependencies": {
    "@microsoft/decorators": "1.20.0",
    "@microsoft/sp-core-library": "1.20.0",
    "@microsoft/sp-dialog": "1.20.0",
    "@microsoft/sp-listview-extensibility": "1.20.0",
    "@microsoft/sp-lodash-subset": "1.20.0",
    "@microsoft/sp-office-ui-fabric-core": "1.20.0",
    "@microsoft/sp-property-pane": "1.20.0",
    "@microsoft/sp-webpart-base": "1.20.0",
    "react": "17.0.1",
    "react-dom": "17.0.1",
    "tslib": "2.3.1"
  },
  "devDependencies": {
    "@microsoft/eslint-config-spfx": "1.20.2",
    "@microsoft/eslint-plugin-spfx": "1.20.2",
    "@microsoft/rush-stack-compiler-4.7": "0.1.0",
    "@microsoft/sp-build-web": "1.20.2",
    "@microsoft/sp-module-interfaces": "1.20.2",
    "@rushstack/eslint-config": "4.0.1",
    "@types/webpack-env": "~1.15.2",
    "ajv": "^6.12.5",
    "eslint": "8.57.0",
    "gulp": "4.0.2",
    "typescript": "4.7.4"
  }
}

Steps to reproduce

From what we can determine, it happens (intermittently) with the following steps:

From our testing, it seems to be the refresh step which then results in the behaviour happening. Without the refresh, we've not been able to reproduce it - but as it's intermittent, that's hard to say for certain!

However...when in the failed state, a page refresh then resolves it.

Expected behavior

For the component rendered via ReactDOM.render(...) to work consistently.