Search Food Item
You can utilize the searchForFood API to find food items based on their taste preferences by providing a search query.
How can I implement the searchForFood
API and retrieve attribute data?
searchForFood
API and retrieve attribute data?import { PassioSDK,} from '@passiolife/nutritionai-react-native-sdk-v2';
/**
* Search the local database of foods with a given search term.
* @param searchQuery - The search term to match against food item names.
* @returns A `Promise` resolving to an array of food item names.
*/
const searchFoods = await PassioSDK.searchForFood(query);
Example
To implement the searchForFood
API and retrieve attribute data, you can modify the useFoodSearch custom hook to include these functionalities. Here's an updated version of your useFoodSearch
hook with added comments and documentation annotations:
useFoodSearch
import { useState, useCallback, useEffect } from 'react';
import { useDebounce } from '../../utils/UseDebounce';
import {
type PassioIDAttributes,
PassioSDK,
type FoodSearchResult,
} from '@passiolife/nutritionai-react-native-sdk-v2';
export interface FoodResult {
passioIDAttributes: PassioIDAttributes;
foodSearchResult: FoodSearchResult;
}
const useFoodSearch = () => {
// State variables
const [searchQuery, setSearchQuery] = useState<string>('');
const [loading, setLoading] = useState<boolean>(false);
const [foodResults, setFoodResults] = useState<FoodResult[]>([]);
const debouncedSearchTerm: string = useDebounce<string>(searchQuery, 300);
// Clears search results and resets state
const cleanSearch = useCallback(() => {
setSearchQuery('');
setFoodResults([]);
setLoading(false);
}, []);
// Calls the search API based on the input value
const callSearchApi = useCallback(
async (query: string) => {
// Check if the query is not empty
if (query.length > 0) {
// Set loading state to indicate the start of the search
setLoading(true);
try {
// Fetch food results from the PassioSDK based on the query
const searchFoods = await PassioSDK.searchForFood(query);
// Process each search result, including fetching attributes
const result: (FoodResult | null)[] = await Promise.all(
searchFoods.map(async (item) => {
// Retrieve attributes for the current food item
const attribute = await PassioSDK.getAttributesForPassioID(
item.passioID
);
// If attributes exist, create a FoodResult object, otherwise, return null
if (attribute) {
return {
passioIDAttributes: attribute,
foodSearchResult: item,
};
} else {
return null;
}
})
);
// Filter out null values (where attributes were not found) and update state
setFoodResults(result.filter((item): item is FoodResult => !!item));
} catch (error) {
// Handle errors, e.g., network issues or API failures
setFoodResults([]);
} finally {
// Reset loading state to indicate the end of the search
setLoading(false);
}
} else {
// If the query is empty, reset the search state
cleanSearch();
}
},
[cleanSearch]
);
// Initiates a new search with the provided query
const onSearchFood = useCallback(
async (q: string) => {
if (q.length > 0) {
setSearchQuery(q);
setFoodResults([]);
} else {
cleanSearch();
}
},
[cleanSearch]
);
// Effect for handling debounced search term changes
useEffect(() => {
if (debouncedSearchTerm.length > 0) {
callSearchApi(debouncedSearchTerm);
} else {
cleanSearch();
}
}, [callSearchApi, debouncedSearchTerm, cleanSearch]);
return {
loading,
foodResults,
searchQuery,
cleanSearch,
onSearchFood,
};
};
export default useFoodSearch;
FoodSearch
import React from 'react';
import {
ActivityIndicator,
FlatList,
Pressable,
StyleSheet,
Text,
TextInput,
} from 'react-native';
import {
PassioIconView,
IconSize,
} from '@passiolife/nutritionai-react-native-sdk-v2';
import { SafeAreaView } from 'react-native-safe-area-context';
import useFoodSearch, { type FoodResult } from './useFoodSearch';
// FoodSearchScreen component
export const FoodSearchScreen = () => {
// Destructure values from the custom hook
const { searchQuery, onSearchFood, foodResults, loading } = useFoodSearch();
// Get styles object from the searchStyle function
const styles = searchStyle();
// Function to render each item in the FlatList
const renderItem = ({ item }: { item: FoodResult }) => {
return (
<Pressable style={styles.itemContainer}>
<PassioIconView
style={styles.itemIcon}
config={{
passioID: item.passioIDAttributes.passioID,
iconSize: IconSize.PX90,
passioIDEntityType: item.passioIDAttributes.entityType,
}}
/>
<Text style={styles.itemFoodName}>{item.foodSearchResult.name}</Text>
</Pressable>
);
};
// Display loading indicator when results are empty and loading is true
const renderLoading = () => {
return <>{loading ? <ActivityIndicator /> : null}</>;
};
// Render the component
return (
<SafeAreaView>
{/* Search input */}
<TextInput
value={searchQuery}
style={styles.textInput}
placeholder="Search Food"
onChangeText={onSearchFood}
/>
{/* FlatList to display search results */}
<FlatList
data={foodResults}
renderItem={renderItem}
ListEmptyComponent={renderLoading}
keyExtractor={(item, index) => item.result.passioID.toString() + index}
/>
</SafeAreaView>
);
};
// Styles for the component
const searchStyle = () =>
StyleSheet.create({
itemContainer: {
padding: 12,
backgroundColor: 'white',
marginVertical: 4,
marginHorizontal: 16,
flexDirection: 'row',
alignItems: 'center',
},
itemFoodName: {
flex: 1,
textTransform: 'capitalize',
marginHorizontal: 8,
fontSize: 16,
},
itemIcon: {
height: 40,
width: 40,
},
textInput: {
backgroundColor: 'white',
paddingHorizontal: 16,
margin: 16,
},
});
Utility Methods
import { useState, useEffect } from 'react';
// Hook
// T is a generic type for value parameter, our case this will be string
export function useDebounce<T>(value: T, delay: number): T {
// State and setters for debounced value
const [debouncedValue, setDebouncedValue] = useState<T>(value);
useEffect(
() => {
// Update debounced value after delay
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
// Cancel the timeout if value changes (also on delay change or unmount)
// This is how we prevent debounced value from updating if value is changed ...
// .. within the delay period. Timeout gets cleared and restarted.
return () => {
clearTimeout(handler);
};
},
[value, delay] // Only re-call effect if value or delay changes
);
return debouncedValue;
}
Result
Last updated