Skip to content

Added support for React.memo#1914

Merged
ljharb merged 2 commits into
enzymejs:masterfrom
jintoppy:master
Feb 26, 2019
Merged

Added support for React.memo#1914
ljharb merged 2 commits into
enzymejs:masterfrom
jintoppy:master

Conversation

@jintoppy

@jintoppy jintoppy commented Nov 22, 2018

Copy link
Copy Markdown
Contributor

This is based on and fixes #1875.

@alexgurr

alexgurr commented Nov 22, 2018

Copy link
Copy Markdown

When I used the code directly from #1875 (comment) (that you have ported to enzyme) it works, but only for top level memo'd components. If there are nested components that are wrapped in memo(), they still display in the DOM as [object Object]. Does this code address that?

I've had to mock every "sub" memo wrapped component using Jest, otherwise the component isn't searchable by name in the DOM.

@ljharb ljharb left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, great start!

Can we make the shallow and mount tests as close to identical as possible?

Let's also add some debug tests for this.

Comment thread packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js Outdated
switch ($$typeofType) {
case ContextConsumer || NaN: return 'ContextConsumer';
case ContextProvider || NaN: return 'ContextProvider';
case Symbol.for('react.memo') || NaN: return 'Memo';

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
case Symbol.for('react.memo') || NaN: return 'Memo';
case REACT_MEMO_TYPE || NaN: return 'Memo';

Comment thread packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx Outdated
Comment thread packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx Outdated
Comment thread packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx Outdated
Comment thread packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx Outdated
@jintoppy

Copy link
Copy Markdown
Contributor Author

@alexgurr Yes. It handles nested memoized components as well.

@jintoppy

Copy link
Copy Markdown
Contributor Author

@ljharb Thanks so much for the quick response and taking time to review. I have made the changes.
REACT_MEMO_TYPE was coming as undefined. So, I added a null check also. Not sure if I am doing that in the right way. Please check and let me know

Comment thread packages/enzyme-adapter-react-16/src/ReactSixteenAdapter.js Outdated
}

function isMemo(node) {
return typeof node.type === 'object' && node.type.$$typeof === REACT_MEMO_TYPE_SYMBOL;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return typeof node.type === 'object' && node.type.$$typeof === REACT_MEMO_TYPE_SYMBOL;
return typeof node.type === 'object' && REACT_MEMO_TYPE && node.type.$$typeof === REACT_MEMO_TYPE;

const wrapper = mount(<Foo foo="qux" />);
expect(wrapper.debug()).to.equal(`<Component foo="qux">
<div>
<InnerComp>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm - i'd expect some sort of indication here that it's memoized.

@ljharb

ljharb commented Nov 26, 2018

Copy link
Copy Markdown
Member

ahhhhh i see that REACT_MEMO_TYPE isn't even exported :-/ https://unpkg.com/react-is@16.6.3/cjs/react-is.development.js

That means we'll have to temporarily hardcode it with var hasSymbol = typeof Symbol === 'function' && Symbol.for; var REACT_MEMO_TYPE = hasSymbol ? Symbol.for('react.memo') : 0xead3;, which is a shame.

@jintoppy

Copy link
Copy Markdown
Contributor Author

@ljharb I will then wait for the PR in react to be merged, and will update this PR.

@TaylorClay

Copy link
Copy Markdown

@jintoppy

Copy link
Copy Markdown
Contributor Author

@Koragan: Sure. will update it soon

@ljharb

ljharb commented Nov 29, 2018

Copy link
Copy Markdown
Member

@Koragan the PR is merged, but react-is still hasn't been updated.

@phenax

phenax commented Dec 3, 2018

Copy link
Copy Markdown

When can we expect this patch to be published? A few of the production project that we're working on depend on it.

@ljharb

ljharb commented Dec 3, 2018

Copy link
Copy Markdown
Member

@phenax first we have to wait until react-is v16 gets updated.

@amite

amite commented Dec 3, 2018

Copy link
Copy Markdown

react-is seems to have support for React Memo: https://github.com/facebook/react/blob/master/packages/react-is/src/ReactIs.js#L120

What exactly is pending? 🤔

@ljharb

ljharb commented Dec 3, 2018

Copy link
Copy Markdown
Member

@amite yes, it's been merged, but not yet released to npm by the react team.

@rpearce

This comment has been minimized.

@jintoppy

This comment has been minimized.

@rpearce

This comment has been minimized.

@MarceloAlves

Copy link
Copy Markdown

react-is 16.7.0 was finally released 🎉

@ljharb

ljharb commented Dec 21, 2018

Copy link
Copy Markdown
Member

I've updated react-is in master and rebased this branch. I'm not sure what's up with the test failures; it might be an issue in enzyme's overall test setup. I'll look into it.

@ljharb ljharb force-pushed the master branch 3 times, most recently from ba622ce to bcbb1e8 Compare December 22, 2018 17:49
@ljharb

ljharb commented Dec 22, 2018

Copy link
Copy Markdown
Member

The underlying problem has been fixed and this branch rebased.

@ljharb ljharb left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Turns out this still needs some work; React itself seems to not render an additional component in the tree for memoized components, so you can't actually .find by a memoized instance directly (unless we unwrap the memoized constructor and search instead for the inner one, and do the reverse in shallow).

I'll try to see what I can do.

@copiali

copiali commented Jan 14, 2019

Copy link
Copy Markdown

Any update?

@ljharb

ljharb commented Feb 7, 2019

Copy link
Copy Markdown
Member

No update yet. I've rebased this; I'll give it another pass tomorrow.

@RoystonS

RoystonS commented Feb 7, 2019

Copy link
Copy Markdown

Btw, there appear to be two React memo tags, not just one: MemoComponent (currently tag 14) and SimpleMemoComponent (currently tag 15). I just got bitten by this on our in-house custom-patched enzyme where we were only coping with tag 15, and I suddenly hit a 14 tag.

I'm not at all sure of the exact details as I only got hit by this a few minutes ago, but it looks like React.memo(fn) gets you a simple component (tag 15) and React.memo(class) gets you a non-simple component (tag 14).

I think the PR at present would only cope with tag 15.

@RoystonS

RoystonS commented Feb 7, 2019

Copy link
Copy Markdown

I've done a bit more digging, and it is the case that there's one tag type for React.memo(FunctionalComponent) and a different tag type for React.memo(ClassComponent)

Details: https://codesandbox.io/s/kwvx4qwxyo (tags are output to the console)

@ljharb

ljharb commented Feb 9, 2019

Copy link
Copy Markdown
Member

I've rebased and updated this to have tests for both class-based and SFC components.

react/react#14807 may be a blocker.

@RoystonS

RoystonS commented Feb 9, 2019

Copy link
Copy Markdown

Btw, React are trying to move away from the term 'SFC':
https://twitter.com/dan_abramov/status/1057625147216220162

@ljharb

ljharb commented Feb 10, 2019

Copy link
Copy Markdown
Member

Per react/react#14807 (comment), the shallow renderer will need to be updated before this can go in as is.

@VincentLanglet

Copy link
Copy Markdown

Any update ?

If it's not easy / possible to make it works for class component, can not we go incrementally ?

Actually it does not work for both functional component and class component. A first implementation working only for functional component is still better.

@ljharb

ljharb commented Feb 22, 2019

Copy link
Copy Markdown
Member

@VincentLanglet no, it'd be better to have zero support than partial support, and either way this is blocked on react's shallow renderer change.

@mattdarveniza

Copy link
Copy Markdown

Support for the case that 99% of people actually want to use memo for (functional components) seems to be a pretty good reason to merge partial support to me. Usage with classes, while supported by React, isn't actually documented anywhere, and I would say it's an edge case or at least very niche.

The failing test is only related to class components so far as I can tell, so if support for that was moved to a separate PR this would be mergeable, we're not waiting on any dependencies for functional components, right?

@ljharb

ljharb commented Feb 26, 2019

Copy link
Copy Markdown
Member

OK, that's a reasonable argument. I suppose it'll just "start working" once react-test-renderer fixes react/react#14807 (comment), at which point we can update the tests here.

@ljharb ljharb merged commit 6b3274b into enzymejs:master Feb 26, 2019
ljharb added a commit that referenced this pull request Feb 26, 2019
 - [new] Added support for React.memo (#1914)
 - [meta] add "directory" field to package.json
 - [refactor] use `displayNameOfNode` instead of `function.prototype.name` directly
switch ($$typeofType) {
case ContextConsumer || NaN: return 'ContextConsumer';
case ContextProvider || NaN: return 'ContextProvider';
case Memo || NaN: return displayNameOfNode(type.type);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

displayName is broken here!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you elaborate? a new issue would be ideal.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

always getting displayName "Component" with React.memo(). need to set correct displayName each time after applying React.memo().

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TymchenkoOleksandr would you mind filing a new issue, and fill out the template? that way I'll be able to fix it more quickly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

New memo feature from React 16.6 is not supported