-
Notifications
You must be signed in to change notification settings - Fork 4
Make two separate queries in same Component #18
Comments
Hi @harshmaur, can you provide us an example use case for that? Maybe explain here what you want to show as a web UI. |
@harshmaur You should be able to achieve this by having two helper instances and two Provider. var mainHelper = helper();
var similarProductsHelper = helper();
mainHelper.on('change', function(state) {
similarProductsHelper.setQuery(state.query).search();
});
const app = <div>
<Provider helper=mainHelper/>
<Provider helper=similarProductsHelper/>
</div> (pseudo code) |
This would help me perform two queries however how do I access each state in my component? I am using connect as mentioned in #16 by @Morhaus import {connect as reduxConnect} from 'react-redux';
import {connect as algoliaConnect} from 'react-algoliasearch-helper';
function mapAlgoliaStateToProps(state) {
return {
results: state.searchResults
};
}
const ReduxConnected = reduxConnect()(ProductSingle);
export default algoliaConnect(mapAlgoliaStateToProps)(ReduxConnected); |
You will not be able to access both states in the same component at the same time, you will only always access one or another given where you put the Results component, see: var mainHelper = helper();
var similarProductsHelper = helper();
mainHelper.on('change', function(state) {
similarProductsHelper.setQuery(state.query).search();
});
const app = <div>
<Provider helper=mainHelper>
<Hits/> // have access to the state for mainHelper Provider
</Provider>
<Provider helper=similarProductsHelper>
<Hits/> // have access to the state for similarProductsHelper Provider
</Provider>
</div> Is that clearer? |
Makes sense now. Thanks! |
Hi @vvo I analysed my whole application and I see that having multiple providers will make structuring the application quite complex. For instance, I have been Adding the provider in my Root.js file wrapping all the components and containers inside it. Its very difficult to wrap a particular section of my container or the container itself. Consider a scenario where I have the same similar products component twice on the same page. Now since the component is same the helper state will be the same however I want to be rendering different products inside of it and thus making two queries. Then i will have to create two separate components and reusability is lost. I am not sure what is the best way to approach the problem. |
@harshmaur You could then always have a single and create a custom widget that would connect to the |
@vvo I could not understand. Which custom widget are you talking about? Also I do not see any query object available to me and I have one index only. |
Hey. I've written a small proof of concept for having a helper whose state is derived from another helper's state. class ChildHelperProvider extends React.Component {
constructor(props) {
super();
this.helper = algoliasearchHelper(client);
this.updateState(props);
}
componentWillReceiveProps(nextProps) {
this.updateState(nextProps);
}
updateState(props) {
this.helper.setState(props.configure(props.searchParameters)).search();
}
render() {
return (
<Provider helper={this.helper}>
{this.props.children}
</Provider>
);
}
}
ChildHelperProvider.propTypes = {
searchParameters: PropTypes.object.isRequired,
configure: PropTypes.func.isRequired,
children: PropTypes.node,
};
const ConnectedChildHelperProvider = connect(state => ({
searchParameters: state.searchParameters
}))(ChildHelperProvider);
// Use it like this (where Results is a connected component):
<Provider helper={helper}>
<div>
<Results />
{/* Also show results for the next page */}
<ConnectedChildHelperProvider configure={s => s.setPage(s.page + 1)}>
<Results />
</ConnectedChildHelperProvider>
</div>
</Provider> The Note that this child helper's state should not be modified directly. If you need to modify it, you should provide a new Note also that the argument of the @harshmaur Could that be useful in your case? |
Yes, I was able to do a second query properly however I am not able to access the new state inside the ConnectedChildHelperProvider. My Container is like this. ProductSingle is already wrapped in the Provider from Root.js class ProductSingle extends React.Component {
constructor(props) {
super();
this.previousHelperState = props.helper.getState();
}
componentDidMount() {
const {helper} = this.props;
helper.setQueryParameter('distinct', false).search();
}
componentWillUnmount() {
this.props.helper.setState(this.previousHelperState).search();
}
render(){
let {results} = this.props;
return(
<div>
{/* Comparison Data.. I get proper results from the first query */}
{results.hits &&
<Container>
<Wrapper title="Compare Prices">
<CompareList data={results.hits} />
</Wrapper>
</Container>
}
{/*Similar Products... I dont get results from the new query in "results" */}
<ConnectedChildHelperProvider
configure={s => s
.removeFacetRefinement("slug")
.addFacetRefinement('category_slug', results.hits[0].category_slug)
.addNumericRefinement('price', '>=', _.toInteger(results.hits[0].price * (1-0.05) ))
.addNumericRefinement('price', '<=', _.toInteger(results.hits[0].price * (1+0.05)))
.setQueryParameter('distinct', true)
}>
<Container>
<Wrapper title="Similar Products">
<Carousel settings = {data.CarouselSettings.tuples}>
{
_.map(results.hits, (item, i)=> {
return (
<ProductTuple key={i} item={item} />
);
})
}
</Carousel>
</Wrapper>
</Container>
</ConnectedChildHelperProvider>
</div>
);
}
}
ProductSingle.propTypes = {
results: PropTypes.object,
helper: PropTypes.object,
};
function mapAlgoliaStateToProps(state) {
console.log(state.searchResults);
return {
results: state.searchResults
};
}
const ReduxConnected = reduxConnect(null, null)(ProductSingle);
export default algoliaConnect(mapAlgoliaStateToProps)(ReduxConnected); |
Hey. _.map(results.hits, (item, i)=> {
return (
<ProductTuple key={i} item={item} />
);
}) In this context, function MyCarousel({hits}) {
if (!hits) {
return null;
}
return (
<Carousel settings = {data.CarouselSettings.tuples}>
{
_.map(hits, (item, i)=> {
return (
<ProductTuple key={i} item={item} />
);
})
}
</Carousel>
);
}
const ConnectedCarousel = connect(state => ({hits: state.searchResults && state.searchResults.hits}))(MyCarousel); And then: <ConnectedChildHelperProvider
configure={s => s
.removeFacetRefinement("slug")
.addFacetRefinement('category_slug', results.hits[0].category_slug)
.addNumericRefinement('price', '>=', _.toInteger(results.hits[0].price * (1-0.05) ))
.addNumericRefinement('price', '<=', _.toInteger(results.hits[0].price * (1+0.05)))
.setQueryParameter('distinct', true)
}>
<Container>
<Wrapper title="Similar Products">
<ConnectedCarousel />
</Wrapper>
</Container>
</ConnectedChildHelperProvider> |
Instead of creating another component I am thinking of using contextTypes since I have already connected the ChildHelperProvider. Here is my file. import React, {PropTypes} from 'react';
import {Provider, connect} from 'react-algoliasearch-helper';
import algoliasearchHelper from 'algoliasearch-helper';
import algoliasearch from 'algoliasearch';
const client = algoliasearch('appID', 'key');
class ChildHelperProvider extends React.Component {
constructor(props) {
super();
this.helper = algoliasearchHelper(client, 'indexname', {
facets: ['category_slug', 'instock']
});
this.updateState(props);
}
getChildContext() {
return {childResults: this.props.childResults};
}
componentWillReceiveProps(nextProps) {
this.updateState(nextProps);
}
updateState(props) {
this.helper.setState(props.configure(props.searchParameters)).search();
}
render() {
return (
<Provider helper={this.helper}>
{this.props.children}
</Provider>
);
}
}
ChildHelperProvider.propTypes = {
searchParameters: PropTypes.object.isRequired,
configure: PropTypes.func.isRequired,
children: PropTypes.node,
childResults: PropTypes.object
};
ChildHelperProvider.childContextTypes = {
childResults: PropTypes.object
};
function mapStateToProps(state) {
// console.log(state.searchParameters);
return {
searchParameters: state.searchParameters,
childResults: state.searchResults && state.searchResults
};
}
export const ConnectedChildHelperProvider = connect(mapStateToProps)(ChildHelperProvider); When I console.log the Moreover when I try to access it in my ProductSingle Container, my contextType childResults returns undefined. It should atleast give me results for the previous query since I can log that. |
The |
A new requirement has come up and I am unable to figure out again. It is basically that I want to pass disjunctiveFacets from my first search to my childHelper. How can we do that? |
I have tried passing it as props to the constructor and instantiating the helper with disjunctive facets, however when I try to see the searchParameters of the new state in the child container, it shows the disjunctiveFacets that I have set for the main helper. My constructor looks like this constructor(props) {
super(props);
let disjunctiveFacets = [];
if (props.disjunc) {
disjunctiveFacets = props.disjunc;
}
this.helper = algoliasearchHelper(client, MAININDEX, {
facets: ['category_slug', 'instock'],
disjunctiveFacets : disjunctiveFacets
});
this.updateState(props);
} |
Hey. You could do <ChildHelperProvider
configure={searchParameters =>
searchParameters
.addFacet('category_slug')
.addFacet('instock')
.addDisjunctiveFacet('my_disjunctive_facet')
}
/> |
Hi, Yes I can, but my problem is not that, if I am not wrong, .addDisjunctiveFacet would only work if I have mentioned that FacetFilter during initialisation of helper. Currently my main helper does not have anything in disjunctiveFacets. However I want to add this to my second search. |
Ah yes, my bad. The For now, you should be able to do:
|
Works!!! Thanks a lot! |
Another Problem, You mentioned earlier that, helper state from child container should not be modified directly, instead it should be done via configure props. I have a usecase where I have to trigger a change in my helper when someone clicks on an item in my child container. What is the best way to tackle this? Do you think I should use redux and create actions for it? |
I figured, that I am not modifying the state, just making a .search or .getState. |
How can I have two search queries made at the same time and retrieve the results? Do I need to maintain Redux state to store the results? or is there any other way?
The text was updated successfully, but these errors were encountered: