-
Notifications
You must be signed in to change notification settings - Fork 47k
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
Support React.memo in ReactShallowRenderer #14816
Conversation
LogError: { FetchError: invalid json response body at http://react.zpao.com/builds/master/_commits/f0621fe232f31cb0fcd63992c3440ec1b4ce5813/results.json reason: Unexpected token < in JSON at position 0
at /home/circleci/project/node_modules/node-fetch/lib/body.js:48:31
at process._tickCallback (internal/process/next_tick.js:68:7)
name: 'FetchError',
message:
'invalid json response body at http://react.zpao.com/builds/master/_commits/f0621fe232f31cb0fcd63992c3440ec1b4ce5813/results.json reason: Unexpected token < in JSON at position 0',
type: 'invalid-json' }
Generated by 🚫 dangerJS |
c32c54f
to
f88d309
Compare
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 are some Flow failures that need fixing, but this looks good to me. :)
If we support |
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.
If we're fixing this we'd need a strategy that works with composing them
@gaearon I've added support for What do you think of the testing strategy? Should I remove |
'Warning: forwardRef requires a render function but received ' + | ||
'a `memo` component. Instead of forwardRef(memo(...)), use ' + | ||
'memo(forwardRef(...))', | ||
{withoutStack: true}, |
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 test is failing saying Expected test not to call console.error()
, but I'm calling toWarnDev
here so I don't think that's right?
const shallowRenderer = createRenderer(); | ||
shallowRenderer.render(<SomeComponent foo={1} />); | ||
expect(areEqual).not.toHaveBeenCalled(); | ||
expect(shallowRenderer.getRenderOutput()).toEqual(<div>1</div>); |
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 test is failing here because the rendered output doesn't match. I verified that getRenderOutput
is returning <div>1</div>
. I guess its failing because they're not the same React elements, but we're using this same pattern elsewhere in this test suite so I don't know why its failing here.
@@ -500,7 +500,8 @@ class ReactShallowRenderer { | |||
element.type, | |||
); | |||
invariant( | |||
isForwardRef(element) || typeof element.type === 'function', | |||
isForwardRef(element) || | |||
(typeof element.type === 'function' || isMemo(element.type)), |
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.
any reason not to use isValidElementType from react-is here, and asserting the type isn’t string, rather than repeating some of the logic?
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.
Probably because isValidElementType
was introduced after this version of the shallow renderer. That's a good point though, because looking at isValidElementType
catches more uncommon cases that React technically supports and that fail with the shallow renderer.
For example, you can do:
const MemoFragment = React.memo(React.Fragment)
But rendering that with the shallow render will trigger this invariant. The same goes for other elements that have their own type like React.Suspense
, React.ConcurrentMode
, React.StrictMode
, etc.
@@ -545,14 +565,13 @@ class ReactShallowRenderer { | |||
} | |||
} | |||
|
|||
if (element.type.hasOwnProperty('contextTypes')) { | |||
if (elementType.hasOwnProperty('contextTypes')) { |
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 may want to use Object.prototype.hasOwnProperty.call here, since it’d otherwise work fine to pass a function component with a null prototype.
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 codepath is for classes so should be ok. In fact it should probably use dot access since that's what we do in the real one.
// elementType could still be a ForwardRef if it was | ||
// nested inside Memo. | ||
this._rendered = | ||
elementType.$$typeof === ForwardRef |
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.
should this use isForwardRef(element)
(and cache the isForwardRef status in a var, since it seems to be checked multiple times)?
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.
It's a little confusing, but that doesn't work because isForwardRef
expects a React Element for the forwardRef
, not the actual object returned from forwardRef
itself.
const forwardRef = React.forwardRef(...);
isForwardRef(forwardRef) // false
isForwardRef(React.createElement(forwardRef)) // true
@gaearon @trueadm how should the shallow renderer handle rendering the element types exported by shallowRenderer.render(<React.StrictMode>hmm</React.StrictMode>) Currently they fail with the error:
I think it's correct that it fails (there's no reason to shallow render them directly) but the error about a
Should we fail with the same error if you try to render Unrelated to this task, but it looks like we also need to update
|
ping on this; I'm going to update enzyme's react 16 adapter so it can handle memo for SFCs, but without this fix it'll still throw for class-based components. It'd be good to close that gap sooner than later. |
I think this is all we should do here. Provide a better error message so they understand that these symbols are not supported. |
ReactShallowRenderer uses element.type frequently, but with React.memo elements the actual type is element.type.type. This updates ReactShallowRenderer so it uses the correct element type for Memo components and also validates the inner props for the wrapped components.
dfb74db
to
2230fab
Compare
* Support React.memo in ReactShallowRenderer ReactShallowRenderer uses element.type frequently, but with React.memo elements the actual type is element.type.type. This updates ReactShallowRenderer so it uses the correct element type for Memo components and also validates the inner props for the wrapped components. * Allow Rect.memo to prevent re-renders * Support memo(forwardRef()) * Dont call memo comparison function on initial render * Fix test * Small tweaks
* Support React.memo in ReactShallowRenderer ReactShallowRenderer uses element.type frequently, but with React.memo elements the actual type is element.type.type. This updates ReactShallowRenderer so it uses the correct element type for Memo components and also validates the inner props for the wrapped components. * Allow Rect.memo to prevent re-renders * Support memo(forwardRef()) * Dont call memo comparison function on initial render * Fix test * Small tweaks
Fixes #14807
Adds support for
React.memo
inReact.ShallowRenderer
. Instead of usingelement.type
directly it now checks if the element isREACT_MEMO_TYPE
and uses the nested type if it is.Validates the propTypes for the inner component
For testing I just coped
ReactShallowRenderer-test
and wrapped every component inReact.memo
to validate everything worked. I don't think we should take approach, but it made it easy to find issues 🤷♂️maybe we can have a single set of tests that run with and without memo?