How does one access state on a nested React component wrapped by an HOC?

12,303

Solution 1

So it seems with the latest release of Enzyme there is a potential fix for this issue of accessing state on a child component.

Let's say we have <Foo> (note the use of React Router's <Link>)

class Foo extends Component {
  state = {
    bar: 'here is the state!'
  }

  render () {
    return (
      <Link to='/'>Here is a link</Link>
    )
  }
}

Note: The following code is only available in Enzyme v3.

Revisiting the test code, we are now able to write the following

it('puts the lotion in the basket', () => {
  const wrapper = mount(
    <MemoryRouter>
      <Foo />
    </MemoryRouter>
  )

  expect(wrapper.find(Foo).instance().state).toEqual({
    bar: 'here is the state!'
  })
})

Using wrapper.find(Child).instance() we are able to access Child's state even though it is a nested component. In previous Enzyme versions we could only access instance on the root. You can also call the setState function on the Child wrapper as well!

We can use a similar pattern with our shallowly rendered tests

it('puts the lotion in the basket shallowly', () => {
  const wrapper = shallow(
    <MemoryRouter>
      <Foo />
    </MemoryRouter>
  )

  expect(wrapper.find(Foo).dive().instance().state).toEqual({
    bar: 'here is the state!'
  })
})

Note the use of dive in the shallow test, which can be run on a single, non-DOM node, and will return the node, shallow-rendered.


Refs:

Solution 2

Thought it might be useful for you guys, as I stumbled upon this and have a fix.

In my case I have a component which is connected to redux.

class ComponentName extends Component {
...
}
export default connect(
  mapStateToProps,
  {
...
 }
)(ComponentName );

connect() is obviously a HOC component. So how do we access the "ComponentName" here?

Very simple:

component
    .find(ComponentName)
    .children()
    .first()
    .props() // returns ComponentName's props 
Share:
12,303

Related videos on Youtube

indiesquidge
Author by

indiesquidge

Updated on June 04, 2022

Comments

  • indiesquidge
    indiesquidge almost 2 years

    I'm using Enzyme, and we can actually use the example component given in the docs as a foundation for my question.

    Let's assume this <Foo /> component uses a <Link> component from ReactRouter and thus we need to wrap it in a <MemoryRouter> for testing.

    Herein lies the problem.

    it('puts the lotion in the basket', () => {
      const wrapper = mount(
        <MemoryRouter>
          <Foo />
        </MemoryRouter>
      )
    
      wrapper.state('name') // this returns null! We are accessing the MemoryRouter's state, which isn't what we want!
      wrapper.find(Foo).state('name') // this breaks! state() can only be called on the root!
    })
    

    So, not exactly sure how to access local component state when using <MemoryRouter>.

    Perhaps I'm performing an ignorant test? Is trying to get/set component state bad practice in testing? I can't imagine it is, as Enzyme has methods for getting/setting component state.

    Just not sure how one is supposed to access the internals of a component wrapped in <MemoryRouter>.

    Any help would be greatly appreciated!

  • xploreraj
    xploreraj about 6 years
    airbnb.io/enzyme/docs/api/ReactWrapper/instance.html Still it shows tells Gets the instance of the component being rendered as the root node passed into mount(), and as I am using mount, still unable to access the state of child component, tells state is not a function. My code is like: let wrapper = mount(<Parent />);
  • xploreraj
    xploreraj about 6 years
  • DarkGuardsman
    DarkGuardsman over 4 years
    Amazed that worked, but is there a way to design the component for testing to avoid needing this?