Skip to content
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

Cant set the props or state of a component inside MemoryRouter #1384

Closed
WalidKurchied opened this issue Nov 26, 2017 · 11 comments
Closed

Cant set the props or state of a component inside MemoryRouter #1384

WalidKurchied opened this issue Nov 26, 2017 · 11 comments

Comments

@WalidKurchied
Copy link

WalidKurchied commented Nov 26, 2017

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( 
    <MemoryRouter>
        <Login agent={{}} submitLogin={loginObject.failingSubmitLoginMock} isAuthenticated={() => false} />
    </MemoryRouter>
);
    wrapper.setProps({agent: agent});

Any one figured a solution to this ?

@ljharb
Copy link
Member

ljharb commented Nov 26, 2017

wrapper.find(Login).setProps()?

@WalidKurchied
Copy link
Author

@ljharb thanks Jordan but i get this error using that line of code: setProps() can only be called on the root :/ any other approaches i can try ?

@ljharb
Copy link
Member

ljharb commented Nov 27, 2017

ah, yes. #361 (which i see you commented on) is indeed relevant.

The solution here tho, is that you'd instead want wrapper.setProps({ children: replacementLoginComponent }), since that's actually the prop that you want to alter.

@WalidKurchied
Copy link
Author

does children refer to the Login component ? and also i'm unclear with what is the replacementLoginComponent meant to be ?

@enniel
Copy link

enniel commented Nov 27, 2017

@WalidKurchied Try this code:

wrapper.setProps({
   children: cloneElement(wrapper.props().children, { agent: agent }),
});

@WalidKurchied
Copy link
Author

@enniel thank you it worked :) .. thanks @ljharb I understand what you meant now.

@drFabio
Copy link

drFabio commented Jan 19, 2018

What about the state? Since you can only set the state on root and root is now MemoryRouter ?

@ljharb
Copy link
Member

ljharb commented Jan 19, 2018

Use shallow for that.

@turnerhayes
Copy link

So if I need to change a prop on a component nested two HOCs deep, I need to do something like clone and set children twice? Are there plans to make this API easier to use?

@tyholby
Copy link

tyholby commented May 9, 2018

I am having a problem with this too. We have multiple levels of HOCs deep. Can anyone help us come up with a better solution than this?

import { deepClone } from 'lodash';

const setNestedProps = (wrapper: any, propsToSet: Object = {}) => {
  // reduxLayer is `wrapper`
  const apolloLayer = wrapper.props().children;
  const felaLayerClone = cloneDeep(apolloLayer.props.children);
  const themeLayer = felaLayerClone.props.children;
  const intlLayer = themeLayer.props.children;
  const muiThemeLayer = intlLayer.props.children;
  const componentLayer = muiThemeLayer.props.children;
  componentLayer.props = Object.assign({}, componentLayer.props, propsToSet);
  wrapper.setProps({
    children: cloneElement(apolloLayer, {
      children: felaLayerClone,
    }),
  });
};

// use of the above method (in a react test file)
setNestedProps(wrapper, {
  users: [user1, user2],
});

@chrisgbaker
Copy link

So @enniel's approach mostly worked (thanks!), but with some strange behavior, possibly just because I don't understand fully. Looking at component.debug();, the above code did successfully update my nested component's props, but lifecycle methods such as componentDidMount were not being fired, and no amount of update or forceUpdate seemed to make that happen. In order for componentDidMount to fire, I had to do this:

    function WrapNavBar_TriggerComponentDidUpdate_ReturnNavBar(displaySavedIndicator: boolean) {
        const wrapper = mount(wrap(<NavigationBar {...navigationBarProps} />));
        wrapper.setProps({
            children: React.cloneElement(wrapper.props().children.props.children, { displaySavedIndicator: false }),
         });
        wrapper.setProps({
        children: React.cloneElement(wrapper.props().children, { displaySavedIndicator }),
        });
        wrapper.update();
        return wrapper.find(NavigationBar);
    }

In the above code, wrap() simply wraps the component inside of an IntlProvider as well as a MemoryRouter. Note that even if i change the first setProps to pass in true for the listed prop, cDM was not being triggered. Also, I'm not certain why, but in the second call to setProps, it seems like a level of the original hierarchy was lost?

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

No branches or pull requests

7 participants