Currently, when some conditional display is needed (like a no results page), you're forced to use the createConnector API.
This API is very internal to react-instantsearchand even if, for performing some conditional display you only touch its surface, it shouldn't be needed by our end user. We need a dedicated API.
I can see several options:
<If condition={(props, searchState) => searchState.query}>QUERY</If>
<If condition={(props, searchState) => !searchState.query}>No Query</If>
else use case. We can use the context to store if a condition has matched. <Choose>
<When condition={(searchState, searchResults) => searchState.query}>QUERY</When>
<Otherwise>NO QUERY</Otherwise>
</Choose>
<Switch>
<When condition={(searchState, searchResults) => searchState.query}>QUERY</When>
<When condition={(searchState, searchResults) => !searchState.query}>NO QUERY</When>
</Switch>
condition is a function that takes searchState and searchResults to still give the maximum of flexibility.
For the record what we use to have before the first release was something like:
<NoResults>, <Results>, <Query>, <NoQuery/>... this wasn't very extensible.
WDYT?
I very like the first approach, what does props contains?
Indeed props is not needed.
I really think it is a great addition, saving people from having to use the connector if all they need is to display a "no results" page.
From an end-user perspective, I would prefer the second one for the sake of clarity: the condition is written only once, and it is made very clear what is going to happen if the condition is met, and what is going to happen if it isn't, all condensed in one place (the <Choose /> component). Cannot be clearer, can it?
We should take inspiration from <Switch /> in React Router
We should take inspiration from
in React Router
Can you provide more details? And maybe enhance the proposal.
It just made me think of that feature, I haven't used it in-depth though, that's why I didn't go more into detail
That's another possible variation. Instead of having an "If, elseIf, else" pattern we can use a switch and render the first valid condition.
<Switch>
<When condition={(searchState, searchResults) => searchState.query}>QUERY</When>
<When condition={(searchState, searchResults) => !searchState.query}>NO QUERY</When>
</Switch>
I'm not sure we should just render the first valid condition and if not, then I don't see much value compare to the <If>.
I'm thinking about this again, and what I think would be interesting here is a connector instead of a component. A component will always feel limited and we will have to make sure that it works with every use case. However something that would work well is:
const MaybeShowResults = ({state, results}) => state.query ? <ShowResults /> : null;
const Results = connectXXX(MaybeShowResults);
const App = () => <Results />
Now this connector could have a few names:
connectConditionalDisplayconnectStateconnectSearchStateThis seems more flexible than a component, since it avoids getting into a proprietary syntax situation
Those components will rely on a connector that will be exposed.
So I guess the question is more => should we just provide the connector to hide createConnector or should we go further to include a declarative syntax ready to use.
Could condition access not only the searchState but also the results? This could also be an opportunity to clearly define a result object shape that we could document (one index, multiple index..).
As I am just back from vacation this could be completely out of scope, let me know :D 🏖
condition would have access to searchState and searchResults yes :)
As much as I love the 1. proposition, it always seems a bit weird to have to use two IFS with the same condition but inverted to be able to do conditional rendering.
Proposal from @Haroenv is good because it adds the lower level layer and at some point if we find it repetitive we can add more higher level structures.
Here's another proposal just for the sake of discussion:
<If
condition={(searchState, searchResults) => {}}
then={() => Results}
else={() => Homepage}
/>
What do you think? :D
@vvo I'm not sure about this one, it looks like a router syntax except that here when you're using the if/else you're more in a middle of you page component and I would expect something more like:
<InstantSearch>
<header/>
<If>
<Results/>
</If>
<Else>
<NoResults/>
</Else>
<InstantSearch/>
Ok, let's go for only the connector for now and see if there's any obvious conditional rendering agnostic library we can showcase in the guide?
I'm wondering what should be the API in case of multi indices.
Let's imagine we have something like:
<InstantSearch
appId="latency"
apiKey="6be0576ff61c053d5f9a3225e2a90f76"
indexName="categories"
>
<SearchBox />
<Index indexName="categories">
<Content>
<CustomCategoriesOrBrands />
</Content>
</Index>
<Index indexName="brands">
<Content>
<CustomCategoriesOrBrands />
</Content>
</Index>
<Index indexName="products">
<Content>
<CustomProducts />
</Content>
</Index>
</InstantSearch>
Should connectConditional let access to all the searchResults and then imply something like:
const Content = connectConditional(
({ searchState, searchResults, children, indexName }) =>
searchResults && searchResults.indexName.nbHits !== 0
? children
: <div>
No results has been found for {searchState.query}
</div>
);
or should we directly provide the searchResults corresponding to the index you're under and then you'll have something like:
const Content = connectConditional(
({ searchState, searchResults, children }) =>
searchResults && searchResults.nbHits !== 0
? children
: <div>
No results has been found for {searchState.query}
</div>
);
cc @vvo @marielaures
Maybe the indexName can be a prop?
Here what I'm wondering is more is there a need of giving access to all searchResults or having the one corresponding to your context enough?
After some thought about it, I believe we should provide all searchResults like we do for autocomplete. You could create a whole no results page taking into account every searchResults at the same time.
@mthuret Aside from conditionally displaying widgets, would their be other usecases for connectConditional? I am asking because it seems to be an easy way to access InstantSearch data without the need of createConnector. What do you think?
After some thought about it, I believe we should provide all searchResults like we do for autocomplete.
Should we still make it easy to get "current index" versus "all other index" results? Like {searchResults, allSearchResults}?
We can have this strategy yes :) Top :)
Apart from conditional display this can be used for other thing yes. Typically if we don't provide a dedicated widget for query rules, you'll use this widget. So maybe connectConditional isn't the right name @vvo ?
So maybe connectConditional isn't the right name @vvo ?
Yes that's the question I asked myself. Since this widget allows you to connect any part of your application to InstantSearch data (state, results) maybe we name it just:
No precise idea but I feel like there's no specific conditional feature, just connecting to data and then using it to do conditional rendering.
connectStateResults at least says to what you get access to. Otherwise could be connectInstantSearchData but a bit long.
connectStateResults
looks good to me too.