I have non-top-level components that use useParams and useSearchParams all over the place to avoid prop drilling, and lots of nested routes. I'm trying to test a dialog that uses some react router hooks and I'm trying to mock stuff using window.history.pushState but I don't understand how this is supposed to work with a router that can't have children.
My common pattern is something like
import {render} from '@testing-library/react';
import {beforeEach} from 'vitest';
beforeEach(() => {
window.history.pushState({}, '', '/route/:routeParam?searchParam=foo')
});
function customRender(children: ReactNode) {
return render(
<Provider1> // if my component needs providers, make a "customRender" function
<Provider2>
{children}
</Provider2>
</Provider1>
);
}
it('should work', () => {
customRender(<ComponentThatUsesParams />)
...
});
Since RouterProvider doesn't accept children, how can I get my components to exist in a context where useParams/useSearchParams will work correctly and not require a direct mock? It could be that my app structure is problematic. Perhaps the useParams in non-top-level components is a bad pattern and I should be prop drilling everywhere? But that seems bad. I have dialogs with deeply nested components that sometimes useNavigate to close the dialog via navigation, which is arguably a bad pattern, but does that mean it's completely untestable? 🤔
I've seen answers regarding use memory router instead of browser router (why? I thought jsdom lets you do the window.history stuff?), but that is not my problem. I'd have this problem no matter what kind of router I used because RouterProvider doesn't accept children.
Feels like I'm misunderstanding something fundamental.