Tengo un cuadro de diálogo de descarga en el que puede hacer clic para descargar los gráficos en mi sitio web. Debido a que los gráficos se crean con HTML Canvas, no puedo simplemente descargar una serie de enlaces de imágenes. Tengo que ponerme en contacto con cada uno de los componentes y convertir cada uno de ellos en un URI de datos.
En el componente de mi aplicación, he definido un contexto para realizar un seguimiento de los componentes que deben descargarse.
export const DownloadFlagsContext = React.createContext<any>(null) const App: React.FunctionComponent = (props) => { const [downloadFlags, setDownloadFlags] = useState([]) return ( <DownloadFlagsContext.Provider value={{downloadFlags, setDownloadFlags}}> <DownloadDialog/> <div className="graphics"> <Graphic id={1}> <Graphic id={2}> <Graphic id={3}> (multiple graphics) </div> </DownloadFlagsContext.Provider> ) }En mi DownloadDialog, desencadeno las descargas configurando downloadFlags en los id de cada uno de los componentes que deben procesarse.
const download = () => { const newDownloadFlags = [] as any for (let i = start; i < end; i++) { newDownloadFlags.push(i) } setDownloadFlags(newDownloadFlags) }Ahora, en cada uno de los componentes gráficos, desencadeno la descarga si se cambiaron los downloadFlags y contiene su id.
const render = () => { const canvas = document.createElement("canvas") /* code to render the graphic */ return canvas.toDataURL("image/png") } useEffect(() => { if (downloadFlags.includes(props.id)) { download("filename", render()) setDownloadFlags(downloadFlags.filter((s: string) => s !== props.id)) } }, [downloadFlags]) El problema es que este código está disparando
descargas Por ejemplo, si configuro la descarga para 6 gráficos, se descargarán 21 imágenes porque cada vez que cambie las banderas de descarga, todos los componentes se volverán a procesar, con la diferencia de que incluye una identificación menos. Así que en total descargará 6+5+4+3+2+1 imágenes. Obviamente esto es muy malo si tengo muchos gráficos para descargar.
Me gustaría evitar que los componentes se vuelvan a renderizar para que solo se descargue las primeras 6 veces.
Además, no quiero usar un servidor. Quiero descargar las imágenes en el lado del cliente.
He descubierto una solución. En el componente de mi aplicación, hago un seguimiento de las ID de descarga, así como de un indicador booleano para habilitar la descarga:
export const DownloadIDsContext = React.createContext<any>(null) export const DownloadFlagContext = React.createContext<any>(null) const App: React.FunctionComponent = (props) => { const [downloadIDs, setDownloadIDs] = useState([]) const [downloadFlag, setDownloadFlag] = useState(false) return ( <DownloadFlagContext.Provider value={{downloadFlag, setDownloadFlag}}> <DownloadURLsContext.Provider value={{downloadURLs, setDownloadURLs}}> <DownloadDialog/> <div className="graphics"> <Graphic id={1}> <Graphic id={2}> <Graphic id={3}> (multiple graphics) </div> </DownloadURLsContext.Provider> </DownloadFlagContext.Provider> ) }En mi DownloadDialog, configuro el indicador booleano en verdadero cuando comienzo una descarga.
const download = () => { const newDownloadIDs = [] as any for (let i = start; i < end; i++) { newDownloadIDs.push(i) } setDownloadIDs(newDownloadIDs) setDownloadFlag(true) }Ahora, en lugar de verificar la matriz de ID de descarga en los componentes secundarios, solo verifico el indicador booleano. Dado que se establece en false inmediatamente, solo hace que los componentes se rendericen una vez.
useEffect(() => { if (downloadFlag) { if (downloadIDs.includes(props.id)) { download("filename", render()) setDownloadIDs(downloadIDs.filter((s: string) => s !== props.id)) setDownloadFlag(false) } } }, [downloadFlag])