1- import React , { useEffect , useRef } from 'react ' ;
1+ import { t } from '@lingui/core/macro ' ;
22import QRCodeStyling , { Options } from 'qr-code-styling' ;
3+ import React , { useCallback , useEffect , useRef } from 'react' ;
34
45// Make all properties optional and add React-specific props
56type StyledQRCodeProps = Partial < Options > & {
67 className ?: string ;
7- download ?: boolean ;
88} ;
99
1010const StyledQRCode : React . FC < StyledQRCodeProps > = ( {
1111 className = '' ,
12- download = false ,
1312 type = 'svg' ,
1413 shape = 'square' ,
1514 width = 300 ,
@@ -38,8 +37,57 @@ const StyledQRCode: React.FC<StyledQRCodeProps> = ({
3837 } ,
3938 ...rest
4039} ) => {
41- const ref = useRef < HTMLDivElement > ( null ) ;
40+ const ref = useRef < HTMLImageElement > ( null ) ;
4241 const qrCode = useRef < QRCodeStyling | null > ( null ) ;
42+ const abortControllerRef = useRef < AbortController | null > ( null ) ;
43+
44+ const updateImageSrc = useCallback ( async ( ) => {
45+ if ( ! qrCode . current || ! ref . current ) return ;
46+
47+ // Cancel any pending operations
48+ if ( abortControllerRef . current ) {
49+ abortControllerRef . current . abort ( ) ;
50+ }
51+ abortControllerRef . current = new AbortController ( ) ;
52+ const signal = abortControllerRef . current . signal ;
53+
54+ try {
55+ const rawData = await qrCode . current . getRawData ( 'svg' ) ;
56+ if ( signal . aborted || ! ref . current ) return ;
57+
58+ if ( rawData instanceof Blob ) {
59+ const dataUrl = await new Promise < string > ( ( resolve , reject ) => {
60+ if ( signal . aborted ) {
61+ reject ( new Error ( 'Aborted' ) ) ;
62+ return ;
63+ }
64+
65+ const reader = new FileReader ( ) ;
66+ reader . onloadend = ( ) => {
67+ if ( signal . aborted || ! ref . current ) {
68+ reject ( new Error ( 'Component unmounted or aborted' ) ) ;
69+ } else {
70+ resolve ( reader . result as string ) ;
71+ }
72+ } ;
73+ reader . onerror = reject ;
74+ reader . readAsDataURL ( rawData ) ;
75+ } ) ;
76+
77+ if ( ! signal . aborted && ref . current ) {
78+ ref . current . src = dataUrl ;
79+ }
80+ }
81+ } catch ( error ) {
82+ if (
83+ error instanceof Error &&
84+ error . message !== 'Aborted' &&
85+ error . message !== 'Component unmounted or aborted'
86+ ) {
87+ console . error ( 'Failed to get QR code data:' , error ) ;
88+ }
89+ }
90+ } , [ ] ) ;
4391
4492 useEffect ( ( ) => {
4593 if ( ! ref . current ) return ;
@@ -59,12 +107,11 @@ const StyledQRCode: React.FC<StyledQRCodeProps> = ({
59107 } ;
60108
61109 qrCode . current = new QRCodeStyling ( options ) ;
62- qrCode . current . append ( ref . current ) ;
110+ updateImageSrc ( ) ;
63111
64- const currentRef = ref . current ;
65112 return ( ) => {
66- if ( currentRef ) {
67- currentRef . innerHTML = '' ;
113+ if ( abortControllerRef . current ) {
114+ abortControllerRef . current . abort ( ) ;
68115 }
69116 } ;
70117 } , [
@@ -79,50 +126,14 @@ const StyledQRCode: React.FC<StyledQRCodeProps> = ({
79126 dotsOptions ,
80127 backgroundOptions ,
81128 rest ,
129+ updateImageSrc ,
130+ // Note: rest is intentionally omitted from dependencies to avoid infinite loops
131+ // The spread operator in the options object will still include rest properties
82132 ] ) ;
83133
84- useEffect ( ( ) => {
85- if ( ! qrCode . current ) return ;
86-
87- const updateOptions : Partial < Options > = {
88- type,
89- shape,
90- width,
91- height,
92- margin,
93- data,
94- qrOptions,
95- imageOptions,
96- dotsOptions,
97- backgroundOptions,
98- ...rest ,
99- } ;
100-
101- qrCode . current . update ( updateOptions ) ;
102- } , [
103- type ,
104- shape ,
105- width ,
106- height ,
107- margin ,
108- data ,
109- qrOptions ,
110- imageOptions ,
111- dotsOptions ,
112- backgroundOptions ,
113- rest ,
114- ] ) ;
115-
116- useEffect ( ( ) => {
117- if ( download && qrCode . current ) {
118- qrCode . current . download ( {
119- extension : type === 'svg' ? 'svg' : 'png' ,
120- name : 'qr-code' ,
121- } ) ;
122- }
123- } , [ download , type ] ) ;
124-
125- return < div ref = { ref } className = { `w-full h-full ${ className } ` } /> ;
134+ return (
135+ < img ref = { ref } className = { `w-full h-full ${ className } ` } alt = { t `QR Code` } />
136+ ) ;
126137} ;
127138
128139export default StyledQRCode ;
0 commit comments