I have been using enzyme and love it a lot. It works with react 16 until I wanted to test my new project that uses react’s new context api.
If I render only my basic component using shallow and console log the debug of the component I can see its content but when I use the new context api with provider and consumer, I get <undefined /> as a render out. Enzyme does not render the component but react does.
Can someone please provide some guidance.
Thank you.
Support for this and other React 16.3 features are going to be included in the next enzyme release which is going to happen in midst summer, this year (as said here).
For now, here's a workaround:
const outer = shallow(<SimpleComp />);
const Children = outer.props().children({ /* context */ });
const wrapper = shallow(Children);
To use mount() with new Context API use this enzyme patch.
Related
When I inspect component structure in React dev tools, I can see that there is a forwardRef mark. I am perplexed because there is no sign of it being used in the source code. How is it that it is there and how can I use it?
The forwardRef calls aren't in your own code, they are in the packages that you are using.
styled.button appears to be from styled-components. This package uses forwardRef to forward any ref that you set on your styled button component to the underlying button component. You can see it in their source code right here:
WrappedStyledComponent = ((React.forwardRef(forwardRef): any): IStyledComponent);
Usage of forwardRef began with styled-components v4 -- see step #2 on this migration guide:
Make sure your application is using react >= 16.3; internally we are using the new React.forwardRef API and new context APIs if you wish to try and polyfill for older React version support
It is also explained on this Tips and Tricks page:
Refs to DOM nodes
styled-components makes use of the new React 16.3 API forwardRef. Thus, set refs like you would on any other component and they will automatically resolve to the underlying DOM element or wrapped component class instance.
I originally followed this project to add Firebase to a Gatsby React app. It involves making a Firebase context, wrapping a root layout with a provider, and then using a withFirebase HOC to wrap components with a Firebase consumer as needed. When I originally did it, it worked fine, but I wanted to move the code into a package I could reuse among my apps. Here's the HOC
export const withFirebase = (Component) => (props) => (
<FirebaseContext.Consumer>
{(firebase) => <Component {...props} firebase={firebase} />}
</FirebaseContext.Consumer>
);
And each page starts with a Layout component that renders this:
<FirebaseContext.Provider value={this.state.firebase}>
<AppWithAuthentication>
{this.props.children}
</AppWithAuthentication>
</FirebaseContext.Provider>
AppWithAuthentication itself uses the withFirebase HOC as it needs Firebase to get the AuthUser (which is then stored in a context and passed down through a provider), and it's able to do so just fine.
All the above happens in the package code itself, but when I imported my package into my other React project, trying to use withFirebase stops working as any components wrapped with it never receive the updated context. I confirm this by checking the Component tree in React Dev tools, the Firebase Provider gets the updated not-null value, and the consumer inside AppWithAuthentication gets it too. But the consumers inside my actual app don't update (and I have this same problem with the AuthUser context I made in the same library).
I even thought that perhaps somehow the parent was rendering with the updated consumer but the children weren't re-rendering, but after counting the renders and logging them it was clear the components from my app were rendering more times than AppWithAuthentication. To make it a bit clearer, here's my component tree (starting from the Layout component at page root):
Here's Provider showing a value:
Here's AppWithAuthentication's consumer showing a value:
And here's the consumer from inside my application that doesn't have a value:
I'm completely stuck here and would appreciate any insight.
EDIT:
After more testing I found some more information but I'm still stuck. It would seem that when reloading my page, the Layout component renders 2 times, the Header and AppWithAuthentication components each renders 4 times, and the Login component renders only 1 time. Is this why the consumers aren't updating? (But then why does the Header component not get any updates when its updating as much as AppWithAuthentication?)
EDIT 2:
After more research, I think this issue has something to do with webpack? I'm using Neutrino.js to make my component library, and I take the output of its build as the library. I found this question that seemed similar and tried implementing the fix like so in my .neutrinorc.js:
const reactComponents = require('#neutrinojs/react-components');
module.exports = {
use: [reactComponents(),
(neutrino) => {
neutrino.config.output.library("upe-react-components");
neutrino.config.output.libraryTarget("umd");
neutrino.config.mode("development");
}],
};
But it didn't fix the issue. Has anyone encountered issues with webpack breaking React context?
I heard from a clever friend of mine that the problem is because React contexts are defined at the module-level, and so since you tried to have your contexts in separate entrypoints, things will not go so well.
I think you can try making an index.js file that re-imports and exports everything, and then things should work as it's all consolidated in one module.
I'm trying to create a Chrome Extension that changes the way a React app behaves. I want to patch a render function of a specific Component.
I thought that the best way would be to Patch the createElement function of React, and once I detect that the component that I want to change is being created, I will patch its render function with my own rendering.
The problem is that this application was built using webpack, and I don't have access to its React instance...
How can I get the React instance?
Thanks!
Any documentation on what's the purpose of adapter in enzyme testing library.
import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
configure({ adapter: new Adapter() });
Any documentation on what's the purpose of adapter in enzyme testing library.
The closest it gets is to say "You will need to install enzyme along with an Adapter corresponding to the version of react (or other UI Component library) you are using".
The docs mostly just explain how to configure an adapter and don't really talk about its purpose.
what is adapter in enzyme
Short version
The enzyme API is the same regardless of the version of React you are using, but how React renders and interacts with what is rendered changes depending on the React version.
The adapter abstracts away anything that changes based on the React version so the core enzyme code can stay the same.
Detailed version
mount and shallow are both exported from enzyme. Let's focus on mount.
mount is a function that just returns a new ReactWrapper.
ReactWrapper provides the familiar wrapper object with instance, setState, find, etc.
The implementation of all of those functions is the same regardless of which version of React you are using...
...but because React itself has changed over the years any implementation details that change based on the React version are abstracted away with an adapter.
The adapter is retrieved by calling getAdapter and the first time it is used is to validate the nodes passed to mount, and then to create the renderer to actually render the nodes.
For enzyme-adapter-react-16.3 that call to createRenderer gets routed to this.createMountRenderer and within createMountRenderer you can see the familiar ReactDOM.render call where what you passed is actually rendered using React v16 syntax.
Searching for getAdapter within ReactWrapper.js shows everywhere that the adapter is used to abstract away functionality that changes based on the React version while using mount...
...and searching for getAdapter within ShallowWrapper.js shows everywhere that adapter is used to abstract away functionality that changes based on the React version while using shallow.
I'm pretty new to React Native and Javascript, I'm currently trying to test methods inside my components, I've seen this being done with Enzyme like
const wrapper = shallow(<Component/>);
wrapper.instance().methodIwannaCall();
Coming from the iOS Dev world, I'm having a hard to understanding why it seems to be so complicated to get an instance of a class and call a method on it.
Unfortunately I'm using React 16.0.0-alpha.12 which means I can't for now use Enzyme, which seems to be the library everyone is using to accomplish what I need.
Also notice I'm not using Redux, I suspect this would be less of a pain if I'd use Redux, since that way all my business logic would be within actions and hence would be easier to test.
Any comments/help are greatly appreciated.
Cheers
You can use pure ReactTestUtils to get instance of your component, for example using renderIntoDocument method:
import ReactTestUtils from 'react-dom/test-utils';
const wrapper = ReactTestUtils.renderIntoDocument(<Component/>);
wrapper.methodIwannaCall();