React-instantsearch: Pass props to HitComponent of Hits/InfiniteHits

Created on 1 Mar 2017  路  5Comments  路  Source: algolia/react-instantsearch

Do you want to request a feature or report a bug?

feature request.

Feature: What is your use case for such a feature?

Our use case is to support customized columns, for example:

// Search Component
class App extends React.Component {
  render() {
    const { columns } = this.state;
    return (
      <InstantSearch
        appId="latency"
        apiKey="3d9875e51fbd20c7754e65422f7ce5e1"
        indexName="bestbuy"
      >
       <Search/>
       <Hits hitComponent={HitComponent} hitComponentProps={{ columns }}
      </InstantSearch>
    );
  }
}

// HitComponent
class HitComponent extends React.PureComponent {
  render() {
    const { hit, columns } = this.props;
    return (
      <div>
        {columns.map(column => (<div key={column}>{hit[column]}</div>))
      </div>
    };
  }
}

What project are you opening an issue for?

  • react-instantsearch

Most helpful comment

Hi @springuper, what about you do this:

// Search Component
class App extends React.Component {
  render() {
    const { columns } = this.state;
    return (
      <InstantSearch
        appId="latency"
        apiKey="3d9875e51fbd20c7754e65422f7ce5e1"
        indexName="bestbuy"
      >
       <Search/>
       <Hits
         hitComponent={hit => <HitComponent hit={hit} columns={this.state.columns} />}
      />
      </InstantSearch>
    );
  }
}

const HitComponent = ({hit, columns}) =>
<div>
  {columns.map(column => <div key={column}>{hit[column]}</div>}
</div>;

?

All 5 comments

Hi @springuper, what about you do this:

// Search Component
class App extends React.Component {
  render() {
    const { columns } = this.state;
    return (
      <InstantSearch
        appId="latency"
        apiKey="3d9875e51fbd20c7754e65422f7ce5e1"
        indexName="bestbuy"
      >
       <Search/>
       <Hits
         hitComponent={hit => <HitComponent hit={hit} columns={this.state.columns} />}
      />
      </InstantSearch>
    );
  }
}

const HitComponent = ({hit, columns}) =>
<div>
  {columns.map(column => <div key={column}>{hit[column]}</div>}
</div>;

?

it's good enough for me, thanks!

Hi,
The solution above works fine by itself, however when I try to wrap <Hits/> using connectStateResults, the application freezes completely and the <Drug/> component is rendered in an infinite loop. Here is the code I'm trying:

function Search({ onDrugClicked }) {
  return (
    <div>
      <div style={{ margin: "auto" }}>
        <SearchBox />
      </div>
      <ListGroup className="mt-2">
        <Content onDrugClicked={onDrugClicked} />
      </ListGroup>
    </div>
  );
}

var Drug = ({ hit, onDrugClicked }) => {
  return (
    <ListGroupItem onClick={onDrugClicked}>
      <DrugCard drug={hit} />
    </ListGroupItem>
  );
};

const Content = connectStateResults(({ searchState, onDrugClicked }) => {
  return searchState && searchState.query ? (
    <Hits
      hitComponent={({ hit }) => (
        <Drug hit={hit} onDrugClicked={() => onDrugClicked(hit)} />
      )}
    />
  ) : (
    <div className="text-muted" />
  );
});

export default () => (
  <InstantSearch
    appId="xxxxxxxxxx"
    apiKey="xxxxxxxx"
    indexName="drugs"
  >
    <Configure hitsPerPage={15} />
    <Search onDrugClicked={hit => console.log(hit)} />
  </InstantSearch>
);

However, when I remove the connectStateResults connector, it works as expected:

function Search({ onDrugClicked }) {
  return (
    <div>
      <div style={{ margin: "auto" }}>
        <SearchBox />
      </div>
      <ListGroup className="mt-2">
        <Content onDrugClicked={onDrugClicked} />
      </ListGroup>
    </div>
  );
}

var Drug = ({ hit, onDrugClicked }) => {
  return (
    <ListGroupItem onClick={onDrugClicked}>
      <DrugCard drug={hit} />
    </ListGroupItem>
  );
};

const Content = ({ searchState, onDrugClicked }) => {
  return true ? (
    <Hits
      hitComponent={({ hit }) => (
        <Drug hit={hit} onDrugClicked={() => onDrugClicked(hit)} />
      )}
    />
  ) : (
    <div className="text-muted" />
  );
};

export default () => (
  <InstantSearch
    appId="xxxxxxxx"
    apiKey="xxxxxxxxxxx"
    indexName="drugs"
  >
    <Configure hitsPerPage={15} />
    <Search onDrugClicked={hit => console.log(hit)} />
  </InstantSearch>
);

Any help is appreciated!

@korroktheslavemaster Seeing the same thing. My Guess is that has to do with passing an anonymous function is creating a new instance of the component every render.

@prescottprue is correct, @vvo's suggestion should be avoided as it's a killer for performance because react sees the function as a new component and therefore mounts and unmounts on each render instead of diffing.

This can be seen by logging in componentDidMount() { console.log('didMount') } or useEffect(() => console.log('didMount'), []).

Most component APIs support passing in componentProps as the OP has suggested or alternatively support a render prop.

In the meantime I've used a custom Hits component as a workaround:

import { connectHits } from 'react-instantsearch-dom';

const Hits: React.FunctionComponent<Props> = ({
    hits,
    active
}) => {
    return (
        <ol style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gridGap: '10px', gridAutoRows: '1fr' }}>
            {hits.map(hit => <li key={hit.objectID}>{hit.objectID} {active}</li>)}
        </ol>
    );
};

export default connectHits(Hits);
Was this page helpful?
0 / 5 - 0 ratings

Related issues

danhodkinson picture danhodkinson  路  3Comments

flouc001 picture flouc001  路  4Comments

tstehle picture tstehle  路  4Comments

mxmzb picture mxmzb  路  4Comments

flouc001 picture flouc001  路  5Comments