instantsearch
instantsearch copied to clipboard
connectStateResults freezes with dynamic hitComponent
Do you want to request a feature or report a bug? Bug
Bug: What is the current behavior? Application freezes; most likely hit component is rendered in a loop
What is the version you are using? Always use the latest one before opening a bug issue. 4.1.3
Here is the example I'm testing on https://codesandbox.io/s/kxlq9608v3
I am using connectStateResults to wrap my Hits to conditionally render them based on search query.
I want to pass props to hitComponent, as mentioned here algolia/react-instantsearch#2018 . Following the solution in that issue, I created a dynamic hitComponent, ie instead of
const Content = connectStateResults(
({ searchState }) =>
<Hits hitComponent={HitComponent}/>
);
I tried
const Content = connectStateResults(
({ searchState }) =>
<Hits hitComponent={({hit}) => (<HitComponent hit={hit}/>)}/>
);
This freezes the application. If I remove the connectStateResults wrapper, it works as expected. Also, this seems to be a problem with connectStateResults only; wrapping in connectHits works. Any help is appreciated!
originally https://github.com/algolia/instantsearch.js/issues/2586
see also https://github.com/algolia/instantsearch.js/issues/2018#issuecomment-345605983
Wondering if it's not the same kind of issue than https://github.com/algolia/instantsearch.js/issues/5269 and more generally the whole connector inside connector issue. One thing weird here is that you're not using anything inside the searchState (loading was the culprit because it's something that change all the time).
@Haroenv @mthuret Is there any update to this? I'm encountering what looks like an infinite loop issue when wrapping a set of refinementList widgets with connectStateResults. The intent is to pass the searching prop so I can conditionally render a spinner when the refinementLists are being updated.
This issue that you describe is the one linked in the previous comment https://github.com/algolia/instantsearch.js/issues/5269. The workaround is to invert the order of the composition (first the connectRefinementList), you can see it in action in the last comment of the thread.
@samouss thanks I will look into it.
@samouss
My situation is a little different, normally I would do as we can see in the comment you reference. But here I have multiple component instances I'm trying to conditionally render, any ideas how to apply the fix to this?
export class DrawerRefinements extends React.Component{
constructor(props){
super(props);
}
render(){
return(
<ScrollView style={{flexDirection: 'column'}}>
<DrawerRangeMultiSlider
header={'Price'}
attributeName={'price'}
precision={2}/>
<Separator/>
<DrawerRangeMultiSlider
attributeName={'temperatureHardiness'}
header={'Temperature Hardiness'}
precision={2}/>
<Separator/>
<DrawerRangeMultiSlider
header={'Full Grown Height'}
attributeName={'fullGrownHeight'}
precision={2}/>
<Separator/>
<DrawerRangeMultiSlider
header={'Full Grown Spread'}
attributeName={'fullGrownSpread'}
precision={2}/>
<Separator/>
<DrawerCategories
attributeName={'companyName'}
header={'Company'}/>
<Separator/>
<DrawerCategories
attributeName={'unit'}
header={'Unit'}/>
<Separator/>
<DrawerCategories
attributeName={'sun'}
header={'Sun'}/>
<Separator/>
<DrawerCategories
attributeName={'water'}
header={'Water'}/>
<Separator/>
<DrawerCategories
attributeName={'bloomColor'}
header={'Bloom Color'}/>
<Separator/>
<DrawerCategories
attributeName={'season'}
header={'Season'}/>
<Separator/>
<DrawerCategories
attributeName={'nativeTo'}
header={'Native To'}/>
<Separator/>
<DrawerCategories
attributeName={'animalResistant'}
header={'Animal Resistant'}/>
</ScrollView>
)
}
}
export const DrawerRefinementsConnected = connectStateResults(({searching}) => {
if(searching){
return(
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
<Spinner isVisible={searching} color={Colors.secondaryColor} type='Bounce'/>
</View>
)
} else {
return(
<DrawerRefinements/>
)
}
})
We provide an other solution inside the thread, instead of unmount the component simply hide it. The issue come from the fact that the component is mount & unmount by hiding it we avoid the problem (see this comment).
@samouss Ah yes, thanks! Its weird to think about rendering things as invisible!
Here's how I did it:
export const DrawerRefinementsConnected = connectStateResults(({searching}) => {
return(
<View style={{flex:1}}>
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
<Spinner isVisible={searching} color={Colors.secondaryColor} type='Bounce'/>
</View>
<View style={searching ? {width: 0, height: 0} : null}>
<DrawerRefinements/>
</View>
</View>
)
})
Having the same issue with [email protected].
Same here.
connectStateResults is infinite loop.
Always returns searching=true case.
const Content = connectStateResults(
({ searchState, searching, onSearchStateChange, getSelected }) => {
if(searching) {
return <Text>
SEACHING...
</Text>
} else {
return (
<InfiniteHits onSearchStateChange={ onSearchStateChange }
searchState={ searchState }
getSelected={ getSelected }/>
)
}
}
);
<InstantSearch
appId="..."
apiKey="..."
indexName="..."
onSearchStateChange={ this.props.onSearchStateChange }
searchState={ this.props.searchState }
refresh={ true }
// resultsState={ { query: '...' } }
>
{ /*<RefinementList attribute="..." headerText={ '...' } />*/ }
<TouchableOpacity
onPress={ () => {
this.props.setModalVisible(false);
} }
>
<Text style={ { marginTop: 20, height: 50, alignSelf: 'center', fontSize: 14 } }>
Close
</Text>
</TouchableOpacity>
<SearchBox />
<Content
onSearchStateChange={ this.props.onSearchStateChange }
searchState={ this.props.searchState }
getSelected={ this.props.getSelected }
/>
</InstantSearch>