instantsearch icon indicating copy to clipboard operation
instantsearch copied to clipboard

connectStateResults freezes with dynamic hitComponent

Open Haroenv opened this issue 8 years ago • 10 comments

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

Haroenv avatar Nov 20 '17 08:11 Haroenv

see also https://github.com/algolia/instantsearch.js/issues/2018#issuecomment-345605983

Haroenv avatar Nov 20 '17 08:11 Haroenv

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).

mthuret avatar Nov 20 '17 09:11 mthuret

@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.

rtman avatar Jan 29 '18 17:01 rtman

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 avatar Jan 29 '18 17:01 samouss

@samouss thanks I will look into it.

rtman avatar Jan 29 '18 17:01 rtman

@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/>
    )
  }
})

rtman avatar Jan 29 '18 17:01 rtman

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 avatar Jan 29 '18 18:01 samouss

@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>
  )
})

rtman avatar Jan 29 '18 18:01 rtman

Having the same issue with [email protected].

m-vdb avatar Apr 18 '18 15:04 m-vdb

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>

wzup avatar Aug 30 '18 18:08 wzup