-
-
Notifications
You must be signed in to change notification settings - Fork 3.4k
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
Enable forcing useLayoutEffect when using alternate renderers #1685
Conversation
Deploy preview for react-redux-docs ready! Built with commit c5925cf |
Noticing that in TravisCI a test fails with
I had this issue locally before providing a babel.config.json to enable dynamic imports. Not sure how to make TravisCI pick this up, or does it need to be configured in a different way (package.json, etc)? |
babel.config.json
Outdated
] | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We already have a .babelrc.js: https://github.com/reduxjs/react-redux/blob/master/.babelrc.js
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I overlooked that. Let me move that over and push that up..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should remove the babel changes. The version of Node we use (14.x) already supports import()
built-in.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And preset-env will also add the dynamic import syntax by default.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is needed for Jest. It is described in Jest documentation https://jestjs.io/docs/en/webpack.html#using-with-webpack-2
It is also discussed in this issue: jestjs/jest#2442
In that thread I just noticed this comment, which may be why TravisCI is failing but the tests run fine locally:
jestjs/jest#2442 (comment)
My tests were failing in CI but not locally the reason for this being that the NODE_ENV was set to production in the Dockerfile. The .babelrc env property is matched against the BABEL_ENV environment variable. Failing that it uses NODE_ENV. Jest will set NODE_ENV to "test" if it is null! Therefore because it was production it wouldn't set it to test and babel would not run the transform.
// Supply a getter just to skip dealing with ESM bindings | ||
export const getUseIsomorphicLayoutEffect = () => useIsomorphicLayoutEffect |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This shouldn't be needed. While imported values are treated as read-only, exported values can change within the module. So, calling forceUseLayoutEffect()
should be fine for the entirety of the execution context.
I would add a forceUseEffect()
as well to allow setting it back.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was under the impression that exported values should change in the importer when changed within the module, but I didn't see that behavior in testing - perhaps an issue in babel? The getBatch
getter exported from utils/reactBatchedUpdates.js
was provided for that exact reason (according to @markerikson).
Consider the following test cases. In each, the module is loaded in a beforeEach block with no window.document.createElement (thus setting useIsomorphicLayoutEffect
to useEffect
). Calling forceUseLayoutEffect
after the module is imported, useIsomorphicLayoutEffect
is useEffect
unless 1.) the module is re-imported or 2. The getter is used.
this works as expected:
it('forces useIsomorphicLayoutEffect to useLayoutEffect', async () => {
forceUseLayoutEffect()
;({ useIsomorphicLayoutEffect } = await import(
'../../src/utils/useIsomorphicLayoutEffect'
))
expect(useIsomorphicLayoutEffect).toBe(useLayoutEffect)
but fails if the re-import is omitted:
it('forces useIsomorphicLayoutEffect to useLayoutEffect', async () => {
forceUseLayoutEffect()
//;({ useIsomorphicLayoutEffect } = await import(
// '../../src/utils/useIsomorphicLayoutEffect'
//))
expect(useIsomorphicLayoutEffect).toBe(useLayoutEffect)
->
Expected: [Function useLayoutEffect]
Received: [Function useEffect]
whereas it passes with the getter:
it('forces useIsomorphicLayoutEffect to useLayoutEffect', async () => {
forceUseLayoutEffect()
expect(getUseIsomorphicLayoutEffect()).toBe(useLayoutEffect)
})
The same behavior was observed when using the "static" import { useIsomorphicLayoutEffect } from ...
This is impressively causing Node to segfault, so is there some looping going on in the tests? That's usually the cause of those kinds of hard crashes. @markerikson Did you want to take a look at this? I consider this more "your" code, since you implemented it originally IIRC. |
Yeah, I asked for this PR, and i do want to look it over. I've just got a lot going on atm and don't have the mental capacity to do so right now. Will try to, Soon (TM) :) |
It is strange that it's seg faulting, before the error was |
My inclination here is that we don't want to allow switching between Problem is, I'm not sure how to restrict that. If we call the getter before the hook is defined, then the file probably gets imported and loaded well before your entry point has a chance to import and call I suppose we could add yet another ref to store that hook reference, but it feels like a waste. |
Heh, this never did get merged. I believe it's obsolete as of v8 because we switched to |
useLayoutEffect
does not work with server-side rendering, and so a warning is displayed if it is used under Node. To get around this warning,useIsomorphicLayoutEffect
tries to detect if the environment is not a browser (via the absence ofwindow.document.createElement
, etc) and falls back touseEffect
if the check fails. Unfortunately, this may cause issues for alternate renderers that run in Node but are not using server-side rendering, such as react-blessed or Ink, and rely on useLayoutEffect to ensure store subscriptions happen at the appropriate phase of rendering.This PR makes available a function
forceUseLayoutEffect
exported fromalternate-renderers
. A client of the react-redux library can call this to force use ofuseLayoutEffect
in any environment.Implementation notes:
forceUseLayoutEffect()
is dynamic and will happen after all of the module files are evaluated (thanks to ES module imports), agetUseIsomorphicLayoutEffect
dynamic getter is provided and existing imports and calls touseIsomorphicLayoutEffect
are replaced with calls to the getterimport(...)
) is enabled via ababel.config.json
file, as dynamic importing is necessary to mimic different import-time environment conditions for testing