typesense-js
typesense-js copied to clipboard
Request to Node 0 failed due to "undefined Network Error" | Error: Exception in HostFunction: Malformed calls from JS: field sizes are different.
Description
I'm using TypesenseInstantSearchAdapter from 'typesense-instantsearch-adapter' and InstantSearch, useInfiniteHits, useSearchBox from 'react-instantsearch-core' in my react native app, when i run the app for ios that part works, but in android don't working
Information about it
That is my dependencies in my package.json
"dependencies": {
"@apollo/client": "^3.8.4",
"@react-native-async-storage/async-storage": "^1.19.3",
"@react-native-firebase/app": "^18.5.0",
"@react-native-firebase/auth": "^18.5.0",
"@react-native-firebase/messaging": "^18.5.0",
"@react-native-masked-view/masked-view": "^0.2.9",
"@react-navigation/bottom-tabs": "^6.2.0",
"@react-navigation/native": "^6.1.8",
"@react-navigation/native-stack": "^6.9.14",
"@reduxjs/toolkit": "^1.9.6",
"@sentry/cli": "^2.22.3",
"@sentry/react-native": "^5.14.1",
"i18next": "^23.5.1",
"mixpanel-react-native": "^2.2.5",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-fast-compare": "^3.2.2",
"react-i18next": "^13.2.2",
"react-instantsearch-core": "^7.3.0",
"react-instantsearch-hooks": "^6.47.3",
"react-native": "^0.72.7",
"react-native-autocomplete-dropdown": "^3.1.0",
"react-native-bootsplash": "^5.1.3",
"react-native-bouncy-checkbox": "^3.0.7",
"react-native-config": "^1.5.1",
"react-native-countdown-circle-timer": "^3.2.1",
"react-native-date-picker": "^4.2.6",
"react-native-device-info": "^10.12.0",
"react-native-geolocation-service": "^5.3.0-beta.4",
"react-native-gesture-handler": "^2.13.1",
"react-native-image-picker": "^5.6.1",
"react-native-image-slider-box": "megamaxs1234/react-native-image-slider-box",
"react-native-indicators": "^0.17.0",
"react-native-keyboard-aware-scroll-view": "^0.9.5",
"react-native-linear-gradient": "^2.8.3",
"react-native-maps": "^1.7.1",
"react-native-mask-input": "^1.2.3",
"react-native-modalize": "^2.0.13",
"react-native-pdf": "^6.7.1",
"react-native-portalize": "^1.0.7",
"react-native-progress": "^5.0.0",
"react-native-ratings": "^8.1.0",
"react-native-reanimated": "^3.6.1",
"react-native-reanimated-carousel": "^3.1.0",
"react-native-safe-area-context": "^4.7.2",
"react-native-screens": "^3.25.0",
"react-native-share": "^9.4.1",
"react-native-skeleton-placeholder": "^5.0.0",
"react-native-star-rating-widget": "^1.2.0",
"react-native-toast-message": "^2.1.8",
"react-native-uuid": "^2.0.1",
"react-native-webview": "^13.5.0",
"react-native-youtube-iframe": "^2.3.0",
"react-redux": "^8.1.3",
"redux": "^4.2.1",
"redux-persist": "^6.0.0",
"tslib": "^2.3.0",
"typesense-instantsearch-adapter": "^2.7.1"
},
Could you share your TypesenseInstantSearchAdapter initialization code?
Could you share your TypesenseInstantSearchAdapter initialization code?
That's my custom hook:
import Config from 'react-native-config';
import TypesenseInstantSearchAdapter from 'typesense-instantsearch-adapter';
export const useSearchVehicle = () => {
const typesenseInstantsearchAdapter = useMemo(() => {
return new TypesenseInstantSearchAdapter({
server: {
nodes: [
{
host: Config.TYPESENSE_HOST, // For Typesense Cloud use xxx.a1.typesense.net
port: 8108, // For Typesense Cloud use 443
protocol: 'http', // For Typesense Cloud use https
},
],
apiKey: Config.TYPESENSE_API_KEY,
connectionTimeoutSeconds: 500,
},
// The following parameters are directly passed to Typesense's search API endpoint.
// So you can pass any parameters supported by the search endpoint below.
// query_by is required.
additionalSearchParameters: {
sort_by: 'typeRecord:asc',
query_by: 'brandName,modelName',
filter_by: 'typeRecord:=MODEL',
},
});
}, [Config])
const searchClient = typesenseInstantsearchAdapter.searchClient;
return {searchClient}
};
And that is my component to search and show the hits:
/* eslint-disable react/no-unstable-nested-components */
import React, {useEffect, useRef, useState} from 'react';
import {
FlatList,
Image,
Keyboard,
TouchableOpacity,
View,
} from 'react-native';
import {Portal} from 'react-native-portalize';
import {Modalize} from 'react-native-modalize';
import Text from "../../../common/components/Text/Text";
import Input from '../../../common/components/Inputs/Input';
import {t} from 'i18next';
import {IModelCarModal} from '../../../vehicles/models/model';
import {IconSearch} from '../../../common/components/Icons/IconSearch';
import {height, resize} from '../../../common/styles/helpers';
import {COLORS} from '../../../common/styles/constants/colors';
import emptyResult from '../../assets/EmptyResult.png';
import style from '../styles';
import {IconRightArrow} from '../../../common/components/Icons/IconRigthArrow';
import useMixpanel from '../../../common/hooks/useMixpanel';
import useMixpanel from '../../../common/hooks/useSearchVehicle';
import {
InstantSearch,
useInfiniteHits,
useSearchBox,
} from 'react-instantsearch-core';
import Config from 'react-native-config';
const SearchVehicleModal: React.FC<IModelCarModal> = props => {
const modalizeRef = useRef<Modalize>(null);
const currentStyle = style();
const [keyboardOffset, setKeyboardOffset] = useState(0);
const [timer, setTimer] = useState<NodeJS.Timeout | null>(null);
const {searchClient}=useSearchVehicle();
const onOpen = () => {
modalizeRef.current?.open();
};
useEffect(() => {
setTimeout(() => {
if (modalizeRef.current) {
onOpen();
}
}, 200);
}, [modalizeRef.current]);
useEffect(() => {
return () => {
if (timer) {
clearTimeout(timer);
}
};
}, [timer]);
const {track} = useMixpanel();
useEffect(() => {
const showSubscription = Keyboard.addListener(
'keyboardDidShow',
onKeyboardShow,
);
const hideSubscription = Keyboard.addListener(
'keyboardDidHide',
onKeyboardHide,
);
return () => {
showSubscription.remove();
hideSubscription.remove();
};
}, []);
const onKeyboardShow = event => {
if (props.carModel.trim() == '') {
setKeyboardOffset(event.endCoordinates.height);
}
};
const onKeyboardHide = () => {
setKeyboardOffset(0);
};
const handleInputChange = (model: string) => {
props.onChange(model);
if (timer) {
clearTimeout(timer);
}
const newTimer = setTimeout(() => {
track('Input Value Entered', {value: model});
}, 2000);
setTimer(newTimer);
};
return (
<Portal>
<InstantSearch
searchClient={searchClient}
future={{ preserveSharedStateOnUnmount: false }}
indexName={Config.TYPESENSE_INDEX_NAME}>
<Modalize
ref={modalizeRef}
// panGestureEnabled={props.carModel.trim() !== ''}
// alwaysOpen={125}
panGestureEnabled={false}
adjustToContentHeight
withOverlay={false}
// alwaysOpen={props.carModel.trim() !== '' ? height / 1.1 : 125}
handlePosition="inside"
closeOnOverlayTap={false}
modalStyle={[
{
borderWidth: resize(1),
borderTopStartRadius: resize(24),
borderTopEndRadius: resize(24),
borderColor: COLORS.GREY,
},
// Platform.OS === 'ios' && {
// bottom: props.carModel.trim() !== '' ? keyboardOffset : undefined,
// },
]}
FooterComponent={() => {
if (props.carModel.trim() === '') {
return null;
}
return (
<TouchableOpacity
onPress={props.onAddManually}
style={currentStyle.footerModalContainer}>
<Text style={currentStyle.footerButtonModal}>
{t('add_car:model_car_modal:add_manually')}
</Text>
</TouchableOpacity>
);
}}
HeaderComponent={<SearchBox handleInput={handleInputChange} />}
>
{props.carModel.trim() !== '' ? (
<InfiniteHits
onPress={props.onPress}
isKeyboardShow={keyboardOffset !== 0}
/>
) : null}
</Modalize>
</InstantSearch>
</Portal>
);
};
function InfiniteHits({...props}) {
const {hits, isLastPage, showMore} = useInfiniteHits();
const currentStyle = style();
return (
<FlatList
data={hits}
style={{height: props.isKeyboardShow ? height / 4 : height / 1.6}}
keyExtractor={item => item.objectID}
onEndReached={() => {
if (!isLastPage) {
showMore();
}
}}
ListHeaderComponent={
<>
{hits.length !== 0 && (
<Text style={currentStyle.subtitleModal}>
{t('add_car:model_car_modal:results')}
</Text>
)}
</>
}
ListEmptyComponent={
<View style={currentStyle.emptyModalContainer}>
<>
<Image source={emptyResult} style={currentStyle.emptyImageModal} />
<Text style={currentStyle.withoutResultsText}>Sin resultados</Text>
<Text style={currentStyle.tryAgainText}>
{t('add_car:model_car_modal:try_search')}
</Text>
</>
</View>
}
renderItem={({item}) => (
<TouchableOpacity
style={currentStyle.itemModalContainer}
key={item.objectID}
activeOpacity={0.6}
onPress={() =>
props.onPress({
brandID: item.brandID,
brandName: item.brandName,
modelName: item.modelName,
objectID: item.objectID,
availableYears: item.availableYears,
})
}>
<Text style={currentStyle.modelCarItem}>
{`${item.brandName} ${item.modelName}`}
</Text>
<IconRightArrow color="#000" size={0} />
</TouchableOpacity>
)}
/>
);
}
function SearchBox(props) {
const {query, refine} = useSearchBox();
const currentStyle = style();
const [inputValue, setInputValue] = useState(query);
const inputRef = useRef(null);
useEffect(() => {
const loadingTimeout = setTimeout(() => {
props.handleInput(inputValue);
refine(inputValue);
}, 500);
return () => {
if (loadingTimeout) {
clearTimeout(loadingTimeout);
}
};
}, [inputValue]);
function setQuery(newQuery: string) {
setInputValue(newQuery);
// props.handleInput(newQuery);
// refine(newQuery);
}
// Track when the InstantSearch query changes to synchronize it with
// the React state.
// We bypass the state update if the input is focused to avoid concurrent
// updates when typing.
// if (query !== inputValue && !inputRef.current?.isFocused()) {
// setInputValue(query);
// }
return (
<View style={[currentStyle.headerModalContainer]}>
<Input
rightIcon={
<View>
<IconSearch />
</View>
}
refs={inputRef}
title={'Busca tu vehículo'}
value={inputValue}
onChangeText={setQuery}
placeholder={'Buscar'}
clearButtonMode="while-editing"
autoCapitalize="none"
autoCorrect={false}
spellCheck={false}
/>
</View>
);
}
export default SearchVehicleModal;
In iOS everything works, the problem is in android