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 ?
wrapper.find(Login).setProps()?
@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 ?
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.
does children refer to the Login component ? and also i'm unclear with what is the replacementLoginComponent meant to be ?
@WalidKurchied Try this code:
wrapper.setProps({
children: cloneElement(wrapper.props().children, { agent: agent }),
});
@enniel thank you it worked :) .. thanks @ljharb I understand what you meant now.
What about the state? Since you can only set the state on root and root is now MemoryRouter ?
Use shallow for that.
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?
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],
});
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?
Most helpful comment
@WalidKurchied Try this code: