From e9f954873af10206e5af51dc4f892a5203d71e38 Mon Sep 17 00:00:00 2001 From: Ryan Christian Date: Thu, 18 Apr 2024 22:59:57 -0500 Subject: [PATCH 1/2] fix: Re-export hooks through compat's `ReactCurrentDispatcher` --- compat/src/render.js | 41 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/compat/src/render.js b/compat/src/render.js index bc56be176a..62dcef0c7e 100644 --- a/compat/src/render.js +++ b/compat/src/render.js @@ -5,6 +5,25 @@ import { toChildArray, Component } from 'preact'; +import { + useCallback, + useContext, + useDebugValue, + useEffect, + useId, + useImperativeHandle, + useLayoutEffect, + useMemo, + useReducer, + useRef, + useState +} from 'preact/hooks'; +import { + useDeferredValue, + useInsertionEffect, + useSyncExternalStore, + useTransition +} from './index'; export const REACT_ELEMENT_TYPE = (typeof Symbol != 'undefined' && Symbol.for && Symbol.for('react.element')) || @@ -264,15 +283,29 @@ options.diffed = function (vnode) { }; // This is a very very private internal function for React it -// is used to sort-of do runtime dependency injection. So far -// only `react-relay` makes use of it. It uses it to read the -// context value. +// is used to sort-of do runtime dependency injection. export const __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = { ReactCurrentDispatcher: { current: { readContext(context) { return currentComponent._globalContext[context._id].props.value; - } + }, + useCallback, + useContext, + useDebugValue, + useDeferredValue, + useEffect, + useId, + useImperativeHandle, + useInsertionEffect, + useLayoutEffect, + useMemo, + // useMutableSource, // experimental-only and replaced by uSES, likely not worth supporting + useReducer, + useRef, + useState, + useSyncExternalStore, + useTransition } } }; From 033f2969a892ce25d9575d788a1791034ecafdb6 Mon Sep 17 00:00:00 2001 From: Ryan Christian Date: Thu, 18 Apr 2024 23:32:31 -0500 Subject: [PATCH 2/2] test: Add simple test for using hooks off of `ReactCurrentDispatcher` --- compat/test/browser/render.test.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/compat/test/browser/render.test.js b/compat/test/browser/render.test.js index df1007bca1..6245fc39fa 100644 --- a/compat/test/browser/render.test.js +++ b/compat/test/browser/render.test.js @@ -544,4 +544,23 @@ describe('compat render', () => { expect(scratch.textContent).to.equal('foo'); }); + + it("should support recoils's usage of __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED", () => { + // Simplified version of: https://github.com/facebookexperimental/Recoil/blob/c1b97f3a0117cad76cbc6ab3cb06d89a9ce717af/packages/recoil/core/Recoil_ReactMode.js#L36-L44 + function useStateWrapper(init) { + const { ReactCurrentDispatcher } = + React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; + const dispatcher = ReactCurrentDispatcher.current; + return dispatcher.useState(init); + } + + function Foo() { + const [value] = useStateWrapper('foo'); + return
{value}
; + } + + React.render(, scratch); + + expect(scratch.textContent).to.equal('foo'); + }); });