-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix: Prevent Yoga init from running more than once #2878
base: master
Are you sure you want to change the base?
Conversation
🦋 Changeset detectedLatest commit: b25e27f The changes in this PR will be included in the next version bump. This PR includes changesets to release 5 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
When quickly calling `loadYoga()` multiple times (in a script that generates PDFs, for example), Yoga would try to initialize multiple times and eventually throw a strange error like: `Expected null or instance of Config, got an instance of Config`
@diegomura |
Any update on this? |
I have the same issue! Sometimes the iFrame remains blank after rendering and I get |
I run into this issue when creating multiple PDFs at the same time with |
@jgo80 Issue is also what I run into. I haven't run @jmezzacappa changes yet but looking at it, it should be alright and quite straightforward to merge. @diegomura could we get a merge? |
Just in case anyone might find this helpful: In my case, we are rendering PDFs in a web server. As a workaround until this PR gets merged/released, I implemented a queue to prevent the server from trying to render multiple PDFs at once. Here's the codeimport { renderToStream } from '@react-pdf/renderer'
import LinkedList from 'mnemonist/linked-list'
import stream from 'node:stream'
import { createElement } from 'react'
import { PDFDoc, type PDFDocProps } from './components'
interface PDFTaskCtx {
pdfProps: PDFDocProps
resolvers: {
resolve: (stream: stream.Readable) => void
reject: (reason: unknown) => void
}
signal: AbortSignal
}
async function renderTask({
pdfProps,
resolvers,
signal,
}: PDFTaskCtx): Promise<void> {
try {
signal.throwIfAborted()
const element = createElement(PDFDoc, pdfProps)
const pdfStream = await renderToStream(element).then(readableStream =>
// react-pdf's stream is typed as a NodeJS.ReadableStream interface, which
// may or may not be a stream.Readable instance.
readableStream instanceof stream.Readable
? stream.addAbortSignal(signal, readableStream)
: stream.Readable.from(readableStream, { objectMode: false, signal }),
)
resolvers.resolve(pdfStream)
// Wait for the stream to finish before moving on to the next PDF
await stream.promises.finished(pdfStream)
} catch (error) {
resolvers.reject(error)
}
}
/** This ensures that only one PDF is rendered at a time */
const enqueueTask = /* @__PURE__ */ (() => {
// An array could work here instead, if you don't expect a lot of tasks to be
// queued up at once. This linked list implementation just provides better
// performance, as both `.push()` and `.shift()` are O(1) operations. For
// small queues, this would make no human-perceivable difference.
const pendingTasks = new LinkedList<() => Promise<void>>()
async function noop() {}
let runTasks = async () => {
// Prevent running multiple times, simultaneously
const runTasksImpl = runTasks
runTasks = noop
try {
while (true) {
const task = pendingTasks.shift()
if (task === undefined) break
await task()
}
} finally {
runTasks = runTasksImpl
}
}
return function enqueueTask(task: () => Promise<void>) {
pendingTasks.push(task)
runTasks()
}
})()
export function renderPdf(
pdfProps: PDFDocProps,
signal: AbortSignal,
): Promise<stream.Readable> {
// Note: Promise.withResolvers is not supported in Node.js versions below v22,
// but it's easy to polyfill/ponyfill.
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/withResolvers
const resolvers = Promise.withResolvers<stream.Readable>()
enqueueTask(() => renderTask({ pdfProps, resolvers, signal }))
return resolvers.promise
} |
Can we merge this or have a beta release? Is there any way to install react-pdf/renderer to test it using this branch? |
Were u able to fix this? |
Any estimates on when this will be released? This error is currently blocking our development, so it would be useful to have some idea on the release date to see if we should wait for it or migrate to another lib. Thanks in advance! |
When quickly calling
loadYoga()
multiple times (in a script or web server that generates PDFs, for example), Yoga would try to initialize multiple times and eventually throw a strange error like:BindingError: Expected null or instance of Config, got an instance of Config
Fixes #2892