react-pdf-highlighter icon indicating copy to clipboard operation
react-pdf-highlighter copied to clipboard

Error: not a valid JSX element

Open raphael10-collab opened this issue 2 years ago • 6 comments

I'm getting this error: not a valid JSX element.

(base) raphy@pc:~/ForgeRaphyTemplate$ yarn start
yarn run v1.22.18
$ electron-forge start
✔ Checking your system
✔ Locating Application
✔ Preparing native dependencies: 4 / 4
✔ Compiling Main Process Code
✔ Launch Dev Servers
⠏ Compiling Preload ScriptsIssues checking in progress...
⠹ Compiling Preload ScriptsERROR in src/app_webpdf/components/App_webpdf.tsx:198:10
TS2786: 'PdfLoader' cannot be used as a JSX component.
  Its instance type 'PdfLoader' is not a valid JSX element.
    Types of property 'refs' are incompatible.
      Type '{ [key: string]: import("/home/raphy/ForgeRaphyTemplate/node_modules/react-pdf-highlighter/node_modules/@types/react/index").ReactInstance; }' is not assignable to type '{ [key: string]: React.ReactInstance; }'.
        'string' index signatures are incompatible.
          Type 'import("/home/raphy/ForgeRaphyTemplate/node_modules/react-pdf-highlighter/node_modules/@types/react/index").ReactInstance' is not assignable to type 'React.ReactInstance'.
            Type 'Component<any, {}, any>' is not assignable to type 'ReactInstance'.
              Type 'import("/home/raphy/ForgeRaphyTemplate/node_modules/react-pdf-highlighter/node_modules/@types/react/index").Component<any, {}, any>' is not assignable to type 'React.Component<any, {}, any>'.
                The types returned by 'render()' are incompatible between these types.
                  Type 'import("/home/raphy/ForgeRaphyTemplate/node_modules/react-pdf-highlighter/node_modules/@types/react/index").ReactNode' is not assignable to type 'React.ReactNode'.
                    Type '{}' is not assignable to type 'ReactNode'.
                      Type '{}' is missing the following properties from type 'ReactPortal': key, children, type, props
    196 |       >
    197 |         
  > 198 |         <PdfLoader url={url} beforeLoad={<Spinner />}>
        |          ^^^^^^^^^
    199 |           {(pdfDocument) => (
    200 |             <PdfHighlighter
    201 |               pdfDocument={pdfDocument}

ERROR in src/app_webpdf/components/App_webpdf.tsx:200:14
TS2786: 'PdfHighlighter' cannot be used as a JSX component.
  Its instance type 'PdfHighlighter<IHighlight>' is not a valid JSX element.
    Types of property 'refs' are incompatible.
      Type '{ [key: string]: import("/home/raphy/ForgeRaphyTemplate/node_modules/react-pdf-highlighter/node_modules/@types/react/index").ReactInstance; }' is not assignable to type '{ [key: string]: React.ReactInstance; }'.
        'string' index signatures are incompatible.
          Type 'import("/home/raphy/ForgeRaphyTemplate/node_modules/react-pdf-highlighter/node_modules/@types/react/index").ReactInstance' is not assignable to type 'React.ReactInstance'.
    198 |         <PdfLoader url={url} beforeLoad={<Spinner />}>
    199 |           {(pdfDocument) => (
  > 200 |             <PdfHighlighter
        |              ^^^^^^^^^^^^^^
    201 |               pdfDocument={pdfDocument}
    202 |               enableAreaSelection={(event) => event.altKey}
    203 |               onScrollChange={resetHash}

This is the complete App_webpdf.tsx file :

import * as React from 'react';

import {
  PdfLoader,
  PdfHighlighter,
  Tip,
  Highlight,
  Popup,
  AreaHighlight,
} from "react-pdf-highlighter";

import type { IHighlight, NewHighlight } from "react-pdf-highlighter";

import { testHighlights as _testHighlights } from "./pdf_highlight/test-highlights";
import { Spinner } from "./pdf_highlight/Spinner";
import { Sidebar } from "./pdf_highlight/Sidebar";

const testHighlights: Record<string, Array<IHighlight>> = _testHighlights

const HighlightPopup = ({
  comment,
}: {
  comment: { text: string; emoji: string };
}) =>
  comment.text ? (
    <div className="Highlight__popup">
      {comment.emoji} {comment.text}
    </div>
  ) : null


interface State {
  url: string;
  highlights: Array<IHighlight>;
}


const parseIdFromHash = () =>
    document.location.hash.slice("#highlight-".length)

const resetHash = () => {
  document.location.hash = ""
}

const getNextId = () =>
    String(Math.random()).slice(2)

// https://nimblewebdeveloper.com/blog/convert-react-class-to-function-component


//const SECONDARY_PDF_URL = "https://arxiv.org/pdf/1604.02480.pdf"
const SECONDARY_PDF_URL = ""


function App_webpdf() {

  const componentIsMounted = React.useRef(true)

  React.useEffect(() => {

    const cb = (event, args) => {
      try {
        if (componentIsMounted.current) {
          console.log("args: ", args)
        }
      } catch (err) {
        console.log("err: ", err)
      }
    }

    // @ts-ignore
    window.api.electronIpcOn("window-webpdf-opened", cb)
    // @ts-ignore
    window.api.electronIpcSend("from-window-webpdf", "This is the WindowWebpdf");

    window.api.electronIpcSend("webpdf-available","");

    return () => { // clean-up function
      componentIsMounted.current = false
      window.api.electronIpcRemoveListener(
        "webpdf-channel",
        cb,
      )
    }
  }, [])


  const [initialurl, setInitialurl] = React.useState("")

  const [state, setState] = React.useState({
    url: "",
    highlights: testHighlights[initialurl]
      ? [...testHighlights[initialurl]]
      : [],
  })

  React.useEffect(() => {

   // @ts-ignore
    window.api.electronIpcOn("window-webpdf-channel-from-main", (event, args) => {
      console.log("App_webpdf-window-webpdf-channel-from-main-args: ", args)
      setInitialurl(args);
    });


    return () => { // clean-up function
    }
  }, [])

  console.log("url-updated: ", initialurl)

  const resetHighlights = () => {
    setState({
      url: initialurl,
      highlights: []
    })
  }

  const toggleDocument = () => {

    const newUrl = 
      state.url === initialurl ? SECONDARY_PDF_URL : initialurl

    setState({
      url: newUrl,
      highlights: testHighlights[newUrl] ? [...testHighlights[newUrl]] : [],
    })
  }


  let scrollViewerTo = (highlight: any) => {}

  const getHighlightById = (id: string) => {
    const { highlights } = state
    return highlights.find((highlight) => highlight.id === id)
  }

  const scrollToHighlightFromHash = () => {
    const highlight = getHighlightById(parseIdFromHash())
    if (highlight) {
      scrollViewerTo(highlight)
    }
  }

  const addHighlight = (highlight: NewHighlight) => {
    const { highlights } = state

    console.log("Saving highlight", highlight)
    setState({
      url: initialurl,
      highlights: [{ ...highlight, id: getNextId() }, ...highlights],
    })
  }

  const updateHighlight = (highlightId: string, position: Object, content: Object) => {
    console.log("Updating highlight: ", highlightId, position, content)

    setState({
      url: initialurl,
      highlights: state.highlights.map((h) => {
        const {
          id,
          position: originalPosition,
          content: originalContent,
          ...rest
        } = h
        return id === highlightId
          ? {
              id,
              position: { ...originalPosition, ...position },
              content: { ...originalContent, ...content },
              ...rest,
            }
          : h;
      })
    })
  }

  const { url, highlights } = state

  return (
    <div className='container' style={{ display: "flex", height: "100vh" }}>

      <Sidebar
        highlights={highlights}
        resetHighlights={resetHighlights}
        toggleDocument={toggleDocument}
      />

      <div
        style={{
          height: "100vh",
          width: "75vw",
          position: "relative",
        }}
      >
        
        <PdfLoader url={url} beforeLoad={<Spinner />}>
          {(pdfDocument) => (
            <PdfHighlighter
              pdfDocument={pdfDocument}
              enableAreaSelection={(event) => event.altKey}
              onScrollChange={resetHash}
              // pdfScaleValue="page-width"
              scrollRef={(scrollTo) => {
                // @ts-ignore
                scrollViewerTo = scrollTo
                scrollToHighlightFromHash()
              }}
              onSelectionFinished={(
                position,
                content,
                hideTipAndSelection,
                transformSelection
              ) => (
                <Tip
                  onOpen={transformSelection}
                  onConfirm={(comment) => {
                    addHighlight({ content, position, comment })
                    hideTipAndSelection()
                  }}
                />
              )}
              highlightTransform={(
                highlight,
                index,
                setTip,
                hideTip,
                viewportToScaled,
                screenshot,
                isScrolledTo
              ) => {
                const isTextHighlight = !Boolean(
                  highlight.content && highlight.content.image
                )
                const component = isTextHighlight ? (
                  <Highlight
                    isScrolledTo={isScrolledTo}
                    position={highlight.position}
                    comment={highlight.comment}
                  />
                ) : (
                  <AreaHighlight
                    isScrolledTo={isScrolledTo}
                    highlight={highlight}
                    onChange={(boundingRect) => {
                      updateHighlight(
                        highlight.id,
                        { boundingRect: viewportToScaled(boundingRect) },
                        { image: screenshot(boundingRect) }
                      )
                    }}
                  />
                )

                return (
                  <Popup
                    popupContent={<HighlightPopup {...highlight} />}
                    onMouseOver={(popupContent) =>
                      setTip(highlight, (highlight) => popupContent)
                    }
                    onMouseOut={hideTip}
                    key={index}
                    children={component}
                  />
                )
             }}
             highlights={highlights}
           />
         )}
       </PdfLoader> 
     </div>
    </div>
  );
}

export default App_webpdf;

Complete webpack configuration :

webpack.renderer.config.js :

const rules = require('./webpack.rules');
const plugins = require('./webpack.plugins');

rules.push({
  test: /\.css$/,
  use: [{ loader: 'style-loader' }, { loader: 'css-loader' }],
});


module.exports = {
  module: {
    rules,
  },
  plugins: plugins,
  resolve: {
    extensions: ['.js', '.ts', '.jsx', '.tsx', '.css'],
    // https://stackoverflow.com/questions/67348426/how-fix-breaking-change-webpack-5-used-to-include-polyfills-for-node-js-core
    fallback: {
      fs: false,
      'stream': require.resolve('stream-browserify'),
      'buffer': require.resolve('buffer/'),
      'util': require.resolve('util/'),
      'assert': require.resolve('assert/'),
      'http': require.resolve('stream-http/'),
      'url': require.resolve('url/'),
      'https': require.resolve('https-browserify/'),
      'os': require.resolve('os-browserify/'),
    },
  },
};

webpack.main.config.js :

module.exports = {
  /**
   * This is the main entry point for your application, it's the first file
   * that runs in the main process.
   */
  //entry: [
    //'./src/main/index.ts',
  //],
  entry: './src/main/index.ts',
  // Put your normal webpack config below here
  module: {
    rules: require('./webpack.rules'),
  },
  resolve: {
    extensions: ['.js', '.ts', '.jsx', '.tsx', '.css', '.json'],
    // https://stackoverflow.com/questions/67348426/how-fix-breaking-change-webpack-5-used-to-include-polyfills-for-node-js-core
    fallback: {
      fs: false,
      'stream': require.resolve('stream-browserify'),
      'buffer': require.resolve('buffer/'),
      'util': require.resolve('util/'),
      'assert': require.resolve('assert/'),
      'http': require.resolve('stream-http/'),
      'url': require.resolve('url/'),
      'https': require.resolve('https-browserify/'),
      'os': require.resolve('os-browserify/'),
    },

  },
};

webpack.plugins.js :

const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyPlugin = require("copy-webpack-plugin")
const path = require('path')
const MiniCssExtractPlugin = require("mini-css-extract-plugin")

module.exports = [
  new ForkTsCheckerWebpackPlugin(),
  new HtmlWebpackPlugin({
    filename: 'index.html',
    template: './src/app/index.html',
    inject:'body',
    chunks: ['app'],
  }),
  // https://github.com/webpack-contrib/mini-css-extract-plugin#recommended
  new MiniCssExtractPlugin({
    // Options similar to the same options in webpackOptions.output
    // all options are optional
  filename: "[name].css",
    chunkFilename: "[id].css",
    linkType: 'text/css',
    ignoreOrder: false, // Enable to remove warnings about conflicting order
  }),
  new CopyPlugin({
    patterns: [
      {
        from: path.resolve(__dirname, "./src/assets/epub"),
        to: path.resolve(__dirname, ".webpack/assets/epub")
      },
      {
        from: path.resolve(__dirname, "./src/assets/svg"),
        to: path.resolve(__dirname, ".webpack/assets/svg")
      },
      {
        from: path.resolve(__dirname, "./src/assets/geojson"),
        to: path.resolve(__dirname, ".webpack/assets/geojson")
      },
      {
        from: path.resolve(__dirname, "./src/assets/pics"),
        to: path.resolve(__dirname, ".webpack/assets/pics")
      },
      {
        from: path.resolve(__dirname, "./src/assets/css"),
        to: path.resolve(__dirname, ".webpack/assets/css")
      },
      {
        from: path.resolve(__dirname, "./node_modules/onnxruntime-web/dist/*.wasm"),
        to: path.resolve(__dirname, "[name][ext]")
      },
      {
        from: path.resolve(__dirname, "./src/assets/onnx-models"),
        to: path.resolve(__dirname, ".webpackassets/onnx-models")
      }

    ]
  })
];

webpack.rules.js :

const MiniCssExtractPlugin = require("mini-css-extract-plugin")

module.exports = [
  // Add support for native node modules
  {
    // We're specifying native_modules in the test because the asset relocator loader generates a
    // "fake" .node file which is really a cjs file.
    test: /native_modules\/.+\.node$/,
    use: 'node-loader',
  },
  {
    test: /\.(m?js|node)$/,
    parser: { amd: false },
    use: {
      loader: '@vercel/webpack-asset-relocator-loader',
      options: {
        outputAssetBase: 'native_modules',
      },
    },
  },
  {
    test: /\.(js|ts)x?$/,
    exclude: /node_modules/,
    use: {
      loader: 'babel-loader',
      options: {
        presets: [
          '@babel/preset-env',
          '@babel/react', {
          'plugins': ['@babel/plugin-proposal-class-properties']}
        ]
      }
    },
  },
  {
    test: /\.tsx?$/,
    exclude: /(node_modules|\.webpack)/,
    use: {
      loader: 'ts-loader',
      options: {
        transpileOnly: true,
      },
    },
  },
  {
    test: /\.(svg)$/i,
    type: 'asset',
    generator: {
      outputPath: '.webpack/assets/svg/'
    },
  },

  {
    test: /\.(png|jpe?g|gif)$/i,
    type: 'asset',
    generator: {
      outputPath: '.webpack/assets/pics/'
    },
  },

  {
    // Font files
    test: /\.(woff|woff2|ttf|otf)$/,
    type: 'asset',
    generator: {
      outputPath: '.wepack/assets/css/'
    },
  },


  {
    test: /\.ts$/,
    include: /src/,
    use: [{ loader: 'ts-loader' }]
  },
  // https://github.com/webpack-contrib/mini-css-extract-plugin#getting-started
  {
    test: /\.(sass|less|css)$/i,
    use: [MiniCssExtractPlugin.loader, "css-loader", "style-loader"],
    generator: {
      outputPath: '.webpack/assets/css/'
    },
  },
  {
    test: /\.pcss$/,
    use: [
      {
        loader: 'style-loader'
      },
      {
        loader: 'css-loader'
      },
      { // https://github.com/webpack-contrib/postcss-loader#getting-started
           loader: 'postcss-loader',
           options: {
                postcssOptions: {
                  plugins: [
                    "postcss-preset-env",
                  ],
                },
                //sourceMap: true,
            },
      },
    ],
  }
];

Other info:

node:  v16.15.0
O.S. : Ubuntu 20.04 Desktop
"webpack": "^5.73.0",
"webpack-bundle-analyzer": "^4.5.0",
"webpack-cli": "^4.9.2"

How to solve the problem? How to make it work?

raphael10-collab avatar Jun 03 '22 15:06 raphael10-collab

What version of react are you using?

milesangelo avatar Jun 29 '22 20:06 milesangelo

This should resolve https://github.com/agentcooper/react-pdf-highlighter/pull/195

milesangelo avatar Jul 01 '22 15:07 milesangelo

@milesangelo Your PR appears to be in error. Would you fix it?

https://github.com/agentcooper/react-pdf-highlighter/runs/7152693658?check_suite_focus=true

yasuhara-shota avatar Jul 19 '22 06:07 yasuhara-shota

Hi! Could anybody share any updates on this issue? Thanks

Denys8 avatar Oct 04 '22 08:10 Denys8

It looks like the problem appears to be the packaged version of the library is somehow deployed using relative paths and once it ends up in my node_modules it fails to load various of its own dependencies

It is attempting to load "PdfLoader.tsx" from "http://localhost:3000/Users/" rather then "http://localhost:3000/Users//code///src/components..." etc

Could not load content for http://localhost:3000/Users/<user>/src/components/PdfLoader.tsx (HTTP error: status code 404, net::ERR_HTTP_RESPONSE_CODE_FAILURE)
Screenshot 2023-01-07 at 12 07 27 PM

As a work around I just pulled in all the source files locally into my project

danrasmuson avatar Jan 07 '23 18:01 danrasmuson

Hi! Still getting this same issue, any way to work around it or some ETA for React 18?

muditmahajan21 avatar May 03 '23 05:05 muditmahajan21