Once in awhile we'll have a test that renders a component P with child C:
<P><C>
<div></div>
<div></div>
</C></P>
To get the second <div> in a test, we'll add a ref to it:
<P><C>
<div></div>
<div ref={'__testDiv'}></div>
</C></P>
//
TestUtils.findRenderedComponentWithType(p, C).refs.testDiv
With enzyme, though, it doesn't seem to be easy to get to that ref
pWrapper.find(C).ref('__testDiv') // Error: ReactWrapper::ref(refname) can only be called on the root
The only alternative has to use css classes, which isn't ideal since that shows up in the final markup. The div itself is pretty hard to select for - is there an easier way I'm missing?
If you're using shallow, does pWrapper.find(C).shallow().ref('__testDiv') work for you?
@ljharb shallow doesn't actually render / have refs, does it? (.ref is undefined). (I tried using mount instead of shallow and calling pWrapper.find(C).mount().ref('__testDiv'), but you can only call .mount() on the root it says)
@ljharb is there a way to make a wrapper instance into a root instance? Doing that, or allowing ref to be called on non-root wrappers seems like something enzyme should support.
Ah, right, sorry - refs are only with mount.
I'd expect .mount() to re-establish what the root is while re-mounting the entire tree from that point.
@ljharb so conceptually, ReactWrapper.mount doesn't to correspond to the top-level mount utility. It just looks like it causes ReactWrapperComponent to render the component if it wasn't already rendered (by unmount).
Would it make sense to return a new instance of ReactWrapper from ReactWrapper.mount() then?
That would make sense to me, since I also assume .shallow() returns a new instance of ShallowWrapper. @lelandrichardson?
Would unmount return a new ReactWrapper too? It just seems potentially confusing since ReactWrapper.mount is paired with ReactWrapper.unmount.
That's a good point too.
I don't think it's obvious to me why .mount() would need to be called again to access child refs. Both from an implementation point of view (any reason why .ref() on a child can't just read starting from the instance pointed to by the wrapper?) and an API consumer point of view (I wouldn't expect to mount() to be the method I have to call, since AFAIK the subcomponent is already mounted with its parent.) Any way we can have .ref(), .instance(), etc work on non-root wrappers?
Currently there's ShallowWrapper.shallow() which returns a new instance of ShallowWrapper that is considered a root, so making ReactWrapper.mount return a new instance of ReactWrapper would be consistent with that.
I think the issue with calling certain methods on non-root wrappers is that React expects all components to return a single root element and wrappers can have ReactWrapper.length > 1, which would complicate things when it comes to, say, calling text() which passes the node to findDOMNode. What would .instance return if it was a wrapper with more than one components?
Maybe @lelandrichardson @ljharb can correct me or expand on that.
I have the same problem - no way to access child component refs while mounting the root component.
No solution yet?
I made something. What do you think ? https://github.com/airbnb/enzyme/pull/672
yormi, I guess having refs with shallow() would be nice but this issue is for accessing child component refs when using mount()
Oh my mistake. The same code with little modifications could be use for mount() though.
Generally speaking, is there a way to access a child component? I am using:
expect(wrapper.find(ChildComponent).state('someState')).to.equal({});
and getting:
Error: ReactWrapper::state() can only be called on the root
Im having the same issue, i need to access a ref inside a child component but have no way to do it. I'd be awesome if this could be added!!!
is this still without solution? :( aiuda
I'm in dire need of this solution as well. I need to do exactly what @joetidee is doing:
expect(wrapper.find(ChildComponent).state('someState')).to.equal({});
@ardok test ChildComponent's state in isolation; you don't need to test it in conjunction with wrapper.
Did anyone ever come to a conclusion on this? I also need the same thing
@gecko25 I wrote a little utils function to do it:
/* Returns a ReactWrapper object for a given ref name
* Params:
* rootComponent: the root component that was mounted
* childComponentName: the name of the child component who has the ref
* refName: the ref to be retrieved
*
* Returns: a ReactWrapper instance of the component assigned to the ref
*/
export function getRef(rootComponent, childComponentName, refName) {
return new ReactWrapper(rootComponent.find(childComponentName).node.refs[refName], rootComponent);
}
(note that that's hardcoded to assume that .refs[refName] is the style you've used in your ref callbacks, which isn't guaranteed)
Try adding a .root
expect(wrapper.find(ChildComponent).root.state('someState')).to.equal({});
@pgriscti that's not valid in enzyme 3.
@ljharb, thanks for the clarification, it looks like _enzyme 3_ was released 4 weeks ago, I'm using _2.9.1_, and it works as expected.
@ljharb is there any viable way to do the same in latest enzyme 3?
@webdevbyjoss avoid using the long-deprecated string refs, and use ref callbacks instead - then do .instance().whatever on the component you want to get a ref from.
any solution yet to
expect(wrapper.find(ChildComponent).state('someState')).to.equal({}); not spitting out
Error: ReactWrapper::state() can only be called on the root
@Persepolis1 expect(wrapper.find(ChildComponent).instance().state).to.have.property('someState', {})?
Thanks @ljharb it worked!
I use Jest, so mine was something like :
expect(component.find('Child').instance().state).toHaveProperty('stateProperty', 'value');
It is important to make the Child a string when using .find(). In other words, find('Child') not find(Child)
@Persepolis1 actually, using a string in find is always a code smell; it's important to avoid doing that. .find(Child) is better if you can import Child into the test and pass the actual constructor there.
I have a Login component thats inside a MemoryRouter component, i'm trying to set the props and even the state of the Login component but it doesn't seem to take effect my guess is that MemoryRouter is blocking it somehow and setting props is actually setting the props of MemoryRouter rather than the Login component i could be wrong but thats what im thinking:
let wrapper = mount(
);
wrapper.setProps({agent: agent});
Any one figured a solution to this ?
Just in case that could help someone, I had kind of the same problem, I was trying to call a child component method with
renderedComponent.find('MyChildComponent').instance().childComponentMethod();
but I was getting this error message
Error: ReactWrapper::instance() can only be called on the root
My workaround was using this instead
renderedComponent.find('MyChildComponent').getNode().childComponentMethod();
You can also access the component state this way.
Hope that helps.
@fallanic Thanks you very much.
It works for me.
Trying to use .instance() in a wrapper test.
@fallanic
Which version of Enzyme is that for? Doesn't seem supported for me in v3.
edit: nm, got it working! 馃帀
mount has .ref(); shallow doesn't have a DOM available (see #672).
Related: #1459, #566.
Closing; please file new issues if there's something more that's needed here.
@fallanic using your solution I got:
ReactWrapper::getNode() is no longer supported. Use ReactWrapper::instance() instead
@ljharb this code does not work for expect(wrapper.find(ChildComponent).instance().state).to.have.property('someState', {})?
Note: My child component is a functional component
do u have any idea to access functional component state?
@mockey-jockey try wrapper.find(ChildComponent).state() - .instance() is an escape hatch that shouldn't be used much, and doesn't even work for an SFC.
@ljharb tried this too wrapper.find(ChildComponent).state() saying null
and in the ChildComponent am using useState hook to set the states. how to access functional component state?
oh, well sure. functional components are stateless, that's why they're called SFCs. useState is not, in fact, component state, it's something different that's confusingly named. React provides no hooks for testing tools to hook into hooks, so there's simply no way for enzyme to access any hook values.
Most helpful comment
Generally speaking, is there a way to access a child component? I am using:
expect(wrapper.find(ChildComponent).state('someState')).to.equal({});and getting: