Skip to content
This repository has been archived by the owner on Nov 17, 2017. It is now read-only.

Usage with React Redux #16

Closed
harshmaur opened this issue Jul 25, 2016 · 4 comments
Closed

Usage with React Redux #16

harshmaur opened this issue Jul 25, 2016 · 4 comments

Comments

@harshmaur
Copy link

I have just started using react and I am trying to integrate algolia in my react application.
I see that the plugin has its own provider and connect which is same as react-redux. How do I integrate both of them?

Thanks!

@alexkirsz
Copy link
Contributor

While react-algoliasearch-helper exposes a similar API to that of react-redux, the connect function and Provider component of the two are different.

To use both in your own project, you'll need to:

  • wrap your root component inside both the react-redux and react-algoliasearch-helper<Provider> components. The order in which you compose them does not matter.
  • when one of your connected components needs to use both the react-redux and the react-algoliasearch-helperconnect functions – that is, it needs to connect to both the redux store's state and the algoliasearch helper's state – you'll need to apply both functions to your component class.

Here's an example on how to use both libraries together:

// Since react-redux and react-algoliasearch-helper expose the same export names, we
// need to alias them in order to import both at the same time.
import {connect as reduxConnect, Provider as ReduxProvider} from 'react-redux';
import {connect as algoliaConnect, Provider as AlgoliaProvider} from 'react-algoliasearch-helper';

function MyComponent(props) {
  return <div />;
}

// Will receive redux's dispatch function
const ReduxConnected = reduxConnect()(MyComponent);

// Will receive the algoliasearch helper
const AlgoliaConnected = algoliaConnect()(MyComponent);

// Will receive both redux's dispatch function and the algoliasearch helper
const AlgoliaAndReduxConnected = algoliaConnect()(ReduxConnected);

ReactDOM.render(
  <ReduxProvider store={store}>
    <AlgoliaProvider helper={helper}>
      <AlgoliaAndReduxConnectedResults />
    </AlgoliaProvider>
  </ReduxProvider>,
  document.getElementById('container')
);

@vvo vvo reopened this Jul 25, 2016
@harshmaur
Copy link
Author

Great it works for me. I have another problem now.

I want to implement infinite scroll in my page. Earlier I have been using Redux state and I append the hits when next page results are called.

I have implemented algolia's connect and removed Redux as I do not need its store anymore. Now everytime I try to get the next page, my previous hits will get updated.

What is the best practice here? I cannot mutate the state returned by algolia.

@alexkirsz
Copy link
Contributor

Contrary to a redux store's state, the algoliasearch helper's state cannot be modified from the outside. It is controlled internally and can only be read through connected components.

You could use React's own state management API in order to keep track of each page's results list.

import React, {PropTypes, Component} from 'react';
import {connect} from 'react-algoliasearch-helper';

class Results extends Component {
  constructor() {
    super();

    this.state = {
      previousHits: []
    };

    this.onLoadMoreClick = this.onLoadMoreClick.bind(this);
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.currentPage === 0) {
      // The search query was modified and the helper reset the page to 0.
      this.setState({
        previousHits: []
      });
    } else if (nextProps.currentPage !== this.props.currentPage) {
      // We've received a new page of hits.
      this.setState({
        previousHits: this.state.previousHits.concat(this.props.currentHits)
      });
    }
  }

  onLoadMoreClick() {
    this.props.helper.setPage(this.props.currentPage + 1).search();
  }

  render() {
    if (!this.props.currentHits) {
      return null;
    }

    // If a search is occuring, it means that the `currentHits` prop is not up
    // to date and has already been persisted in state.
    const currentHits = this.props.searching ? [] : this.props.currentHits;
    const hits = this.state.previousHits.concat(currentHits);
    return (
      <div>
        <ul>
          {hits.map(hit =>
            <li>{JSON.stringify(hit)}</li>
          )}
        </ul>
        <button onClick={this.onLoadMoreClick}>
          Load more
        </button>
      </div>
    );
  }
}

Results.propTypes = {
  helper: PropTypes.object.isRequired,
  searching: PropTypes.bool.isRequired,
  currentPage: PropTypes.number.isRequired,
  // currentHits is not required as it is not present until the first search
  // has successfully completed.
  currentHits: PropTypes.array
};

const ConnectedResults = connect(state => ({
  searching: state.searching,
  currentPage: state.searchParameters.page,
  currentHits: state.searchResults && state.searchResults.hits
}))(Results);

export default ConnectedResults;

See the official doc on the subject on componentWillReceiveProps, setState and more generally on Interactivity and Dynamic UIs.

Note that you could achieve the same result by storing your previousHits cache inside a redux store.

Please let me know if that solution works for you :)

@harshmaur
Copy link
Author

Thanks a lot, this is exactly what I had before in redux. I will store the previous hits in Redux itself. I thought if there could be a better way and avoid Redux completely.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants