kozakdenys / qr-code-styling

Automaticly generate your styled QR code in your web app.
https://qr-code-styling.com
MIT License
1.34k stars 432 forks source link

Importing QRCodeStyling into NextJS immediately fails #38

Open Ali762 opened 3 years ago

Ali762 commented 3 years ago

Using version 1.3.4

Simply adding import QRCodeStyling from "qr-code-styling";

causes the package to immediately fail with server error: ReferenceError: self is not defined

The library does not check to verify it's running client side.

Workaround for now is to replace the import statement with if (typeof window !== "undefined") { console.log("i am client"); const QRCodeStyling = require("qr-code-styling"); }

Jmg-21 commented 3 years ago

same issue encountered! @Ali762 can you share how to use the code inside the component after that import am getting "self is not defined at Object. (/.../qr-code-styling/lib/qr-code-styling.js:1:208)"

Ali762 commented 3 years ago

I got it working like this. I'm not sure why I used qrCode update - you might be able to use your data directly in the options.

export function QR( mydata) {
  let qrCode;
  if (typeof window !== "undefined") {           //Only do this on the client
    const QRCodeStyling = require("qr-code-styling");
    qrCode = new QRCodeStyling({
      width: 600,
      height: 600,
      data: "yourdata",
     ...etc

then

 qrCode.update({
    data: mydata,
  });
Jmg-21 commented 3 years ago

@Ali762 thank you! it works with no delay. another note is when you use it with ssr:false it will have delay creating 1 and if more than 1 lets say 100 qr at the same time will only create 2 qr's. but with this way. it solve the issue

jromero commented 3 years ago

FWIW, I ran into this issue "self is not defined" on Node JS because this library has number of dependencies on the browser.

I eventually got it to work by emulating various components. Here's what worked at this point in time:

// emulate browser dependencies
const Blob = require("cross-blob");
const Canvas = require("canvas");
const { JSDOM } = require("jsdom");
global.Blob = Blob;
global.window = new JSDOM().window;
global.self = global.window;
global.document = global.window.document;
global.Image = Canvas.Image;
global.XMLSerializer = global.window.XMLSerializer;

// swallow not implemented location changes (https://github.com/jsdom/jsdom/issues/2112#issuecomment-673540137)
const listeners = window._virtualConsole.listeners('jsdomError');
const originalListener = listeners && listeners[0];
window._virtualConsole.removeAllListeners('jsdomError');
window._virtualConsole.addListener('jsdomError', error => {
  if (
    error.type !== 'not implemented' &&
    error.message !== 'Not implemented: navigation (except hash changes)' &&
    originalListener
  ) {
    originalListener(error);
  }
  // swallow error
});
jasonbarry commented 3 years ago

Should be solved by webpack's output.globalObject config setting:

To make UMD build available on both browsers and Node.js, set output.globalObject option to 'this'.

SamSamskies commented 2 years ago

I just ran into this issue and I solved it by using dynamic imports with the ssr config option set to false. https://nextjs.org/docs/advanced-features/dynamic-import#with-no-ssr

rtorcato commented 2 years ago

I created a react hook to use QRCodeStyling in NextJS. The following code should help it also uses typescript.

import React, { useEffect, useRef, useState } from 'react'

import DashboardLayout from '@app/layouts/DashboardLayout'
import { IPage } from '@app/ts/nextJS.types'
import PageContainer from '@app/components/containers/PageContainer'
import QRCodeStyling, { Options as QRCodeStylingOptions, FileExtension } from 'qr-code-styling'
import CardContainer from '@app/components/containers/CardContainer'
import Button from '@app/components/buttons/Button'

const styles = {
  inputWrapper: {
    margin: '20px 0',
    display: 'flex',
    justifyContent: 'space-between',
    width: '100%',
  },
  inputBox: {
    flexGrow: 1,
    marginRight: 20,
  },
}

const qrOptions: QRCodeStylingOptions = {
  width: 300,
  height: 300,
  image: 'https://upload.wikimedia.org/wikipedia/commons/5/51/Facebook_f_logo_%282019%29.svg',
  dotsOptions: {
    color: '#4267b2',
    type: 'rounded',
  },
  imageOptions: {
    crossOrigin: 'anonymous',
    margin: 20,
  },
}

const useQRCodeStyling = (options: QRCodeStylingOptions): QRCodeStyling | null => {
  //Only do this on the client
  if (typeof window !== 'undefined') {
    // eslint-disable-next-line @typescript-eslint/no-var-requires
    const QRCodeStylingLib = require('qr-code-styling')
    const qrCodeStyling: QRCodeStyling = new QRCodeStylingLib(options)
    return qrCodeStyling
  }
  return null
}

const QrCodePage: IPage = () => {
  const [url, setUrl] = useState('https://cardstore.matrixdigital.com')
  const [fileExt, setFileExt] = useState<FileExtension | undefined>('png')
  const qrCode = useQRCodeStyling(qrOptions)
  const ref = useRef<any>(null)

  useEffect(() => {
    qrCode?.append(ref.current)
  }, [ref, qrCode])

  useEffect(() => {
    qrCode?.update({ data: url })
  }, [url, qrCode])

  const onUrlChange: React.ChangeEventHandler<HTMLInputElement> | undefined = (event) => {
    event.preventDefault()
    setUrl(event.target.value)
  }

  const onExtensionChange: React.ChangeEventHandler<HTMLSelectElement> | undefined = (event) => {
    setFileExt(event.target.value as FileExtension)
  }

  const onDownloadClick = () => {
    qrCode?.download({ extension: fileExt })
  }
  return (
    <PageContainer>
      <CardContainer>
        <div className="m-3">
          <h1 className="text-lg text-medium">QR Codes</h1>
          <div className="my-5">
            <div style={styles.inputWrapper}>
              <input value={url} onChange={onUrlChange} style={styles.inputBox} />
              <select onChange={onExtensionChange} value={fileExt}>
                <option value="png">PNG</option>
                <option value="jpeg">JPEG</option>
                <option value="webp">WEBP</option>
              </select>
              <Button theme="primary" onClick={onDownloadClick}>
                Download
              </Button>
            </div>
            <div ref={ref} />
          </div>
        </div>
      </CardContainer>
    </PageContainer>
  )
}
QrCodePage.getLayout = (page) => <DashboardLayout>{page}</DashboardLayout>

export default QrCodePage
zhen1asemen1uk commented 1 year ago

Wow, it's working! Thank you so much. I wasted a lot of time. You are my hero 💙💛

multikitty commented 1 year ago

hello, @rtorcato . it's working for me. Thanks. But I have an issue now. When I build my next app, the following error shows me.

const QRCodeStylingLib = require('qr-code-styling');

Error: Unexpected require(). global-require

How to fix it?

rtorcato commented 1 year ago

hello, @rtorcato . it's working for me. Thanks. But I have an issue now. When I build my next app, the following error shows me.

const QRCodeStylingLib = require('qr-code-styling');

Error: Unexpected require(). global-require

How to fix it?

this should solve it. It should just be eslint error

https://stackoverflow.com/a/37487154/1213956

multikitty commented 1 year ago

Thanks, @rtorcato

minikdev commented 7 months ago

thanks @rtorcato it works

madhusudanbabar commented 1 month ago

I created a react hook to use QRCodeStyling in NextJS. The following code should help it also uses typescript.

import React, { useEffect, useRef, useState } from 'react'

import DashboardLayout from '@app/layouts/DashboardLayout'
import { IPage } from '@app/ts/nextJS.types'
import PageContainer from '@app/components/containers/PageContainer'
import QRCodeStyling, { Options as QRCodeStylingOptions, FileExtension } from 'qr-code-styling'
import CardContainer from '@app/components/containers/CardContainer'
import Button from '@app/components/buttons/Button'

const styles = {
  inputWrapper: {
    margin: '20px 0',
    display: 'flex',
    justifyContent: 'space-between',
    width: '100%',
  },
  inputBox: {
    flexGrow: 1,
    marginRight: 20,
  },
}

const qrOptions: QRCodeStylingOptions = {
  width: 300,
  height: 300,
  image: 'https://upload.wikimedia.org/wikipedia/commons/5/51/Facebook_f_logo_%282019%29.svg',
  dotsOptions: {
    color: '#4267b2',
    type: 'rounded',
  },
  imageOptions: {
    crossOrigin: 'anonymous',
    margin: 20,
  },
}

const useQRCodeStyling = (options: QRCodeStylingOptions): QRCodeStyling | null => {
  //Only do this on the client
  if (typeof window !== 'undefined') {
    // eslint-disable-next-line @typescript-eslint/no-var-requires
    const QRCodeStylingLib = require('qr-code-styling')
    const qrCodeStyling: QRCodeStyling = new QRCodeStylingLib(options)
    return qrCodeStyling
  }
  return null
}

const QrCodePage: IPage = () => {
  const [url, setUrl] = useState('https://cardstore.matrixdigital.com')
  const [fileExt, setFileExt] = useState<FileExtension | undefined>('png')
  const qrCode = useQRCodeStyling(qrOptions)
  const ref = useRef<any>(null)

  useEffect(() => {
    qrCode?.append(ref.current)
  }, [ref, qrCode])

  useEffect(() => {
    qrCode?.update({ data: url })
  }, [url, qrCode])

  const onUrlChange: React.ChangeEventHandler<HTMLInputElement> | undefined = (event) => {
    event.preventDefault()
    setUrl(event.target.value)
  }

  const onExtensionChange: React.ChangeEventHandler<HTMLSelectElement> | undefined = (event) => {
    setFileExt(event.target.value as FileExtension)
  }

  const onDownloadClick = () => {
    qrCode?.download({ extension: fileExt })
  }
  return (
    <PageContainer>
      <CardContainer>
        <div className="m-3">
          <h1 className="text-lg text-medium">QR Codes</h1>
          <div className="my-5">
            <div style={styles.inputWrapper}>
              <input value={url} onChange={onUrlChange} style={styles.inputBox} />
              <select onChange={onExtensionChange} value={fileExt}>
                <option value="png">PNG</option>
                <option value="jpeg">JPEG</option>
                <option value="webp">WEBP</option>
              </select>
              <Button theme="primary" onClick={onDownloadClick}>
                Download
              </Button>
            </div>
            <div ref={ref} />
          </div>
        </div>
      </CardContainer>
    </PageContainer>
  )
}
QrCodePage.getLayout = (page) => <DashboardLayout>{page}</DashboardLayout>

export default QrCodePage

Hey, Thanks for this, I'm having similar issue in Nuxt 3, I took reference from your dynamic import thing and it worked well.

Here's the snippet for Nuxt 3

let qrCode;

onMounted(async () => {
  if (!window) return;
  const qrCodeLib = (await import("qr-code-styling")).default;
  qrCode = new qrCodeLib(options);
});