diff --git a/package.json b/package.json index d664da7..d8190e7 100644 --- a/package.json +++ b/package.json @@ -27,13 +27,13 @@ "freeze" ], "peerDependencies": { - "react": ">=17.0.0" + "react": ">=19.0.0" }, "devDependencies": { "@types/jest": "^25.1.4", "@types/node": "^12.12.38", - "@types/react": "^18.0.15", - "@types/react-test-renderer": "^18.0.0", + "@types/react": "^19.0.0", + "@types/react-test-renderer": "^19.0.0", "@typescript-eslint/eslint-plugin": "^5.3.0", "@typescript-eslint/parser": "^5.3.0", "babel-eslint": "^10.0.3", @@ -51,7 +51,7 @@ "microbundle-crl": "^0.13.10", "npm-run-all": "^4.1.5", "prettier": "^2.0.4", - "react": "^18.2.0", + "react": "^19.2.0", "react-scripts": "^3.4.1", "react-test-renderer": "^18.2.0", "typescript": "^3.7.5" diff --git a/src/index.tsx b/src/index.tsx index b6a2693..519ba02 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,6 +1,4 @@ -import React, { Suspense, Fragment } from "react"; - -const infiniteThenable = { then() {} }; +import React, { Suspense, Fragment, use } from "react"; interface SuspenderProps { freeze: boolean; @@ -8,9 +6,33 @@ interface SuspenderProps { } function Suspender({ freeze, children }: SuspenderProps) { - if (freeze) { - throw infiniteThenable; + // Create a stable promise which we can later use to suspend the component. + const promiseRef = React.useRef>(null); + // Ref to store the promise's resolver function, to be called when un-freezing. + const resolverRef = React.useRef<() => void>(null); + if (promiseRef.current === null && freeze) { + promiseRef.current = new Promise((resolve) => { + resolverRef.current = resolve; + }); + } + + if (!freeze && resolverRef.current != null) { + // Un-freeze: call the resolver to resolve the promise and un-suspend. + resolverRef.current(); + resolverRef.current = null; + } + + if (promiseRef.current !== null) { + // Suspend by using the promise, or when promise resolved it un-suspends. + use(promiseRef.current); + } + + if (!freeze) { + // Only reset promise here, as when un-freezing we want to "ping the attached listeners" that the promise is resolved. + // Thats why we call the resolver above when !freeze. + promiseRef.current = null; } + return {children}; } diff --git a/yarn.lock b/yarn.lock index a875adf..2dae09b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2734,10 +2734,10 @@ resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.5.tgz#75a2a8e7d8ab4b230414505d92335d1dcb53a6df" integrity sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ== -"@types/react-test-renderer@^18.0.0": - version "18.0.0" - resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-18.0.0.tgz#7b7f69ca98821ea5501b21ba24ea7b6139da2243" - integrity sha512-C7/5FBJ3g3sqUahguGi03O79b8afNeSD6T8/GU50oQrJCU0bVCCGQHaGKUbg2Ce8VQEEqTw8/HiS6lXHHdgkdQ== +"@types/react-test-renderer@^19.0.0": + version "19.1.0" + resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-19.1.0.tgz#1d0af8f2e1b5931e245b8b5b234d1502b854dc10" + integrity sha512-XD0WZrHqjNrxA/MaR9O22w/RNidWR9YZmBdRGI7wcnWGrv/3dA8wKCJ8m63Sn+tLJhcjmuhOi629N66W6kgWzQ== dependencies: "@types/react" "*" @@ -2750,14 +2750,12 @@ "@types/scheduler" "*" csstype "^3.0.2" -"@types/react@^18.0.15": - version "18.0.15" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.15.tgz#d355644c26832dc27f3e6cbf0c4f4603fc4ab7fe" - integrity sha512-iz3BtLuIYH1uWdsv6wXYdhozhqj20oD4/Hk2DNXIn1kFsmp9x8d9QB6FnPhfkbhd2PgEONt9Q1x/ebkwjfFLow== +"@types/react@^19.0.0": + version "19.2.7" + resolved "https://registry.yarnpkg.com/@types/react/-/react-19.2.7.tgz#84e62c0f23e8e4e5ac2cadcea1ffeacccae7f62f" + integrity sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg== dependencies: - "@types/prop-types" "*" - "@types/scheduler" "*" - csstype "^3.0.2" + csstype "^3.2.2" "@types/resolve@0.0.8": version "0.0.8" @@ -4957,6 +4955,11 @@ csstype@^3.0.2: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.9.tgz#6410af31b26bd0520933d02cbc64fce9ce3fbf0b" integrity sha512-rpw6JPxK6Rfg1zLOYCSwle2GFOOsnjmDYDaBwEcwoOg4qlsIVCN789VkBZDJAGi4T07gI4YSutR43t9Zz4Lzuw== +csstype@^3.2.2: + version "3.2.3" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.2.3.tgz#ec48c0f3e993e50648c86da559e2610995cf989a" + integrity sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ== + cyclist@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" @@ -10965,12 +10968,10 @@ react-test-renderer@^18.2.0: react-shallow-renderer "^16.15.0" scheduler "^0.23.0" -react@^18.2.0: - version "18.2.0" - resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" - integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== - dependencies: - loose-envify "^1.1.0" +react@^19.2.0: + version "19.2.0" + resolved "https://registry.yarnpkg.com/react/-/react-19.2.0.tgz#d33dd1721698f4376ae57a54098cb47fc75d93a5" + integrity sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ== read-pkg-up@^2.0.0: version "2.0.0"