在不触发重新渲染的情况下更改 React 上下文

Change React Context without triggering a re-render

我有一个下载对话框,您可以单击它来下载我网站上的图形。因为图形是用 HTML Canvas 创建的,所以我不能简单地下载一组图像链接。我必须联系每个组件并将它们分别呈现到数据 URI 中。

在我的应用程序组件上,我定义了一个上下文来跟踪应该下载哪些组件。

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>
    )
}

在我的 DownloadDialog 中,我通过将 downloadFlags 设置为需要呈现的每个组件的 ID 来触发下载。

const download = () => {
        const newDownloadFlags = [] as any
        for (let i = start; i < end; i++) {
            newDownloadFlags.push(i)
        }
        setDownloadFlags(newDownloadFlags)
}

现在,在每个 Graphic 组件中,如果 downloadFlags 已更改且包含其 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])

问题在于此代码正在触发 下载。例如,如果我将下载设置为 6 个图形,它将导致下载 21 个图像,因为每次我更改 downloadFlags 时,所有组件都将重新呈现,不同之处在于它包含的 id 少了一个。所以总共会下载 6+5+4+3+2+1 张图片。显然,如果我有很多图形要下载,这是非常糟糕的。

我想阻止组件重新渲染,以便它只下载前 6 次。

另外,我不想使用服务器。我想在客户端下载图片

我想出了解决办法。在我的应用程序组件中,我跟踪下载 ID 以及用于启用下载的布尔标志:

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>
    )
}

在我的 DownloadDialog 中,我在开始下载时将布尔标志设置为 true。

const download = () => {
        const newDownloadIDs = [] as any
        for (let i = start; i < end; i++) {
            newDownloadIDs.push(i)
        }
        setDownloadIDs(newDownloadIDs)
        setDownloadFlag(true)
}

现在我不再检查子组件中的下载 ID 数组,而是只检查布尔标志。由于它立即设置为 false,它实际上只导致组件渲染一次。

useEffect(() => {
      if (downloadFlag) {
        if (downloadIDs.includes(props.id)) {
            download("filename", render())
            setDownloadIDs(downloadIDs.filter((s: string) => s !== props.id))
            setDownloadFlag(false)
}
        }
}, [downloadFlag])