We are building an SPA which uses Algolia for search-related features. While we periodically renew users apiKey, cases might happen where the key is expired (user reopens his laptop and starts searching right away before any renewal can be made).
I've searched through the documentation but could not find a way to hook an onError callback or even detect when there are similar issues.
Does anyone have pointers?
A regular error should be thrown when API key is incorrect. In that case you can use an Error boundary around the <InstantSearch />
You can also use the connector connectStateResults to access the error.
@Haroenv I tried using React error boundaries with success:
class SearchContainer extends React.Component {
componentDidCatch(error) {
debugger
// renew searchToken
}
render() {
const { searchToken, searchState, searchApplicationId } = this.props
if (!searchToken) {
return false
}
const query = cleanSearchQuery(searchState.get('query'))
return (
<InstantSearch
appId={searchApplicationId}
apiKey="obviously-invalid" //{searchToken}
indexName="notes"
root={{
Root: 'div',
props: {
className: 'flexbox flexbox-no-grow flexbox-no-shrink',
style: { width: 300 },
},
}}
searchState={{ query, hitsPerPage: HITS_PER_PAGE }}
>
<SearchPanel />
</InstantSearch>
)
}
}
I can see the 403s in the debug tools but the debugger isn't stopping and no logs show in my console. This isn't so surprising as I guess these network errors are asynchronous and can't be caught by React (unless InstantSearch specifically rethrows them in a way React can catch).
I coupled it with @samouss solution and it worked but it feels clumsy:
class SearchContainer extends Component {
componentDidCatch(error, info) {
debugger
// renew searchToken
}
render() {
const { searchToken, searchState, searchApplicationId } = this.props
if (!searchToken) {
return false
}
const query = cleanSearchQuery(searchState.get('query'))
return (
<InstantSearch
appId={searchApplicationId}
apiKey="obviously-invalid" //{searchToken}
indexName="notes"
root={{
Root: 'div',
props: {
className: 'flexbox flexbox-no-grow flexbox-no-shrink',
style: { width: 300 },
},
}}
searchState={{ query, hitsPerPage: HITS_PER_PAGE }}
>
<SearchErrorCatcher />
<SearchPanel />
</InstantSearch>
)
}
}
const SearchErrorCatcher = connectStateResults(
class SearchErrorCatcher extends Component {
error = null
componentWillReceiveProps(nextProps) {
if (this.error && !nextProps.error) {
this.error = null
}
if (!this.error && nextProps.error) {
// only throw on new errors
throw nextProps.error
}
}
render() {
return null
}
}
)
@ArnaudRinquin Thanks for sharing your solution. At the end you don't even need to re-throw the error. You can call a function that will renew the apiKey directly from willReceiveProps.
Oh I know I don't have to, but it feels cleaner.
Anyway, It'd be nice to have a proper Error handling as this solution seems clunky. @Haroenv don't you think the Error Boundary not working is a bug and should be addressed? An onError prop on the InstantSearch could work as well.
The error boundaries are used to catch error related to the rendering. This one is not related to the rendering, you can recover it independently from React (see Error Boundaries).
You are right a prop onError could also work, I like the approach with the declarative SearchErrorCatcher. It act a bit like an error boundaries at the end, it catch the network error of InstantSearch. We could provide a widget out of the box it will feels a bit more natural.
Most helpful comment
@ArnaudRinquin Thanks for sharing your solution. At the end you don't even need to re-throw the error. You can call a function that will renew the
apiKeydirectly fromwillReceiveProps.