Describe the bug π
This is similar to #883. However my issue is not that hits is undefined, but rather than data within my hits are initially undefined when passed to my custom hit component.
Each of my hits have a categories field, which is an array of objects:
hit.categories: [
{ id, title },
β¦
]
In my custom hit component, Iβm pulling out the first category index to highlight it:
<div>{categories[0].title}</div>
This initially loads fine within InfiniteHits. However, whenever the Configure component changes the filter property of the search, the app blows up because categories is being passed as undefined. The only way I can get things to not break is to short-circuit every use of categories data:
<div>{categories && categories[0].title}</div>
This solves the app blowing up since the data does eventually get passed in, but introduces unnecessary complexity/ambiguity in the code.
Am I perhaps doing something wrong, or not leveraging some part of the API/components that I don't know about yet?
(For what it's worth, I've tried rendering InfiniteHits only when hits are available, just in case that was causing the issue, but no dice. The issue appears to be initially incomplete data at the level of individual hits when passed to the hit component.)
To Reproduce π
Annoyingly, I have not been able to reproduce this in a Code Sandbox π .
Hereβs the (abbreviated) structure of my app, for reference:
<Router>
<InstantSearch>
<Switch>
<Route path='/category1'>
<Configure filter='type:X'>
<InfiniteHits hitComponent={TypeXComponent} />
</Route>
<Route path='/'>
<InfiniteHits hitComponent={AllTypesComponent} />
</Route>
</Switch>
</InstantSearch>
</Router>
Expected behavior π
Hits passed to a hit component should be fully serialized when passed in.
Environment:
Where is the custom component rendering <div>{categories[0].title}</div>? is that inside hitComponent?
@Haroenv That's right, it's part of the hitComponent.
The issue appears to be initially incomplete data at the level of individual hits when passed to the hit component
Reading this again, are you sure that all of your hits actually have categories set? You can check that in the devtools on your site where the errors are thrown.
It's better in this case to be overly safe around attributes that possibly don't exist at runtime, since otherwise a small mistake in data entry could break your site
@Haroenv Yes, the type of hits I'm filtering for all have categories set. In fact, we have a condition to only index these items if they have a category attached to them. So unfortunately this isn't an issue of data entry. π
I think I'm on to something.
When I switch between these routes of my app, I'm triggering a new search (via <Configure filters='β¦' />. However, my routes switch faster than my search hits update. Since the new set of hits hasn't come back yet, and when the route switches the old set of hits are still in memory, the _new_ InfiniteHits component (within that new route) is trying to render _all the old hits_ with a hitComponent that's looking for hit props that only _some_ of those hits will have. Since it's trying to render _all_ the hits, and not all those hits have those props, it's blowing up.
So, I need to find a way to only load that InfiniteHits/hitComponent when the _new_ set of hits has been returned (or, on each hitComponent, have it return null if it gets called with a hit type that it can't handle, which sounds silly, but would do the trick).
I'm wondering if maybe I'm handling the combination of routing and filtering in a sub-optimal way? Maybe there's a more programmatic way to handle this? If not, I'll close this issue and sort it out in my components.
That's a good point, infinite hits will render with the previous hits, even if the filters no longer match, to avoid flickering or showing no hits.
A first option I see is checking the searching boolean in connectStateResults for the hits: https://codesandbox.io/s/sad-thompson-0lrgg?file=/src/App.js
A second option is to render the specific hit type based on what its props are, not on what the state is. That way you can be sure it will always have the properties you expect.
Does that make sense?
Thanks for the suggestions! I'll look into these and a couple other options. Either way, this isn't a bug with React Instant Search, so I'll close the issue. Thanks again!
Just in case anyone else ends up with this issue, this is the solution I used:
On each route, where I mount a new InfiniteHits component, each of which use their own hitComponent, I just check the hit type before assigning a hit component:
<Route path='/typeX'>
<InfiniteHits hitComponent={({ hit }) => hit.type === 'X' ? <HitComponentX hit={hit} /> : null />
</Route>
<Route path='/'>
<InfiniteHits hitComponent={GenericHitComponent} />
</Route>
(I'm using a type attribute, but this could be adapted for any case where InfiniteHits uses a hit component based on an arbitrary hit attribute.)
This prevents my hit components from rendering for hits which they aren't built for, without having to sort that logic out within the individual hit components. This also prevents having to hook into the search state, which avoided the issues @Haroenv mentioned of flickering and no hits.