microsoft / powerbi-client-react

Power BI for React which provides components and services to enabling developers to easily embed Power BI reports into their applications.
MIT License
308 stars 98 forks source link

ReferenceError: self/window is not defined when building NextJS app #110

Closed alvarogonoring closed 7 months ago

alvarogonoring commented 7 months ago

As some of you already known, this issue isn't a new one, but also i've run out of options... already tried the NextJs dynamic import, safe hydration and the use of the useEffect hook as suggested in other past issues.

After running script next build, the follow error is returned (compiling locally is all good):

.ReferenceError: self is not defined
    at ./node_modules/window-post-message-proxy/dist/windowPostMessageProxy.js (node_modules\powerbi-client\dist\powerbi.js:1293
0:44)
    at __webpack_require__ (node_modules\powerbi-client\dist\powerbi.js:12955:42)
    at ./src/factories.ts (node_modules\powerbi-client\dist\powerbi.js:8552:35)
    at __webpack_require__ (node_modules\powerbi-client\dist\powerbi.js:12955:42)
    at node_modules\powerbi-client\dist\powerbi.js:12980:17
    at node_modules\powerbi-client\dist\powerbi.js:13023:3
    at node_modules\powerbi-client\dist\powerbi.js:13026:12
    at webpackUniversalModuleDefinition (node_modules\powerbi-client\dist\powerbi.js:6:20)
    at Object.<anonymous> (node_modules\powerbi-client\dist\powerbi.js:13:3)
    at Module._compile (node:internal/modules/cjs/loader:1376:14)

> Build error occurred
Error: Failed to collect page data for /dashboards/dashboard-embed
    at node_modules\next\dist\build\utils.js:1220:15
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
  type: 'Error'
}

Even all of this togheter as you can see in the code below:

'use client'

import {apiGet, apiPost} from "@/config/api";
import {useEffect, useState} from "react";
import {models} from "powerbi-client";
import dynamic from "next/dynamic";

interface Props {
  id: number;
}

const isBrowser = typeof window !== 'undefined' && typeof self !== 'undefined';

export default function DashboardEmbed({id}: Props) {

  const [powerBIData, setPowerBIData] = useState({
    reportId: undefined,
    token: undefined
  });
  const [progress, setProgress] = useState(0);

  const fetchDashboard = async (id: number) => {
    setProgress(50);

    await apiGet(`/dashboard/${id}`, {}).then(async (dashboardData: any) => {
      setProgress(100);
      const {token} = await apiPost<any>('/bi-config/token', {
        dashboardId: dashboardData?.id
      });

      setPowerBIData({
        reportId: dashboardData?.reportId,
        token
      });
    });
  }

  let PowerbiEmbedded: any = null;

  const PowerBIDashboard = ({data}: any) => {
    if (isBrowser && data.token) {
      PowerbiEmbedded = dynamic(() => import('nextjs-powerbi').then(component => component.default), {
        ssr: false,
      })

      return (
          <div>
            {
              isBrowser && data.token &&
              <PowerbiEmbedded
                    id={data.reportId}
                    embedUrl={`https://app.powerbi.com/reportEmbed?reportId=${data.reportId}`}
                    accessToken={data.token}
                    filterPaneEnabled={false}
                    navContentPaneEnabled={false}
                    embedType="report"
                    tokenType={models.TokenType.Embed}
                    className="embedContainer"
                    height={850}
                    width={1470}
              />
            }
          </div>
      )
    }
  }

  useEffect(() => {
    if (id && isBrowser) {
      fetchDashboard(id)
    }
  }, [id]);

  return (
      <>
        {
          isBrowser && powerBIData.token &&
          <PowerBIDashboard data={powerBIData}/>
        }
      </>
  )
}
alvarogonoring commented 7 months ago

Apparently the piece of code using the NextJS dynamic import is not wrong at all, but it needs to be executed outside the scope of the React component, this way:

let PowerbiEmbedded;

PowerbiEmbedded = dynamic(() => import('nextjs-powerbi').then(component => component.default), {
        ssr: false,
      })

export default function MyReactComponent() {
        return (
                <PowerbiEmbedded />
        )
}