Recipe

Display the nutritional information for a recipe using the getAttributesForPassioID API and obtain the corresponding nutrient data

/**
     * Look up the nutrition attributes for a given Passio ID.
     * @param passioID - The Passio ID for the attributes query.
     * @returns A `Promise` resolving to a `PassioIDAttributes` object if the record exists in the database or `null` if not.
     */
    getAttributesForPassioID(passioID: PassioID): Promise<PassioIDAttributes | null>;
 

Example

To generate the nutritional information for a recipe, the initial step involves obtaining the FoodItems and subsequently computing and aggregating the values for all nutrients.

useRecipe

import { useState, useEffect } from 'react';
import {
  PassioSDK,
  type PassioFoodItem,
} from '@passiolife/nutritionai-react-native-sdk-v2';
import {
  getNutrients,
  getNutrientsFromRecipe,
  mergeNutrients,
  type Nutrients,
  type NutrientsTypes,
} from '../../../utils/NutrientsUtils';

// Mock ingredients for testing purposes
const mockIngredients = ['1603211584879'];

// Nutrient types required for the recipe
const requireNutrientTypes: NutrientsTypes[] = [
  'calories',
  'carbs',
  'protein',
  'fat',
];

export const useRecipeEditor = () => {
  // State variables for the recipe editor
  const [recipeName, setRecipeName] = useState<string>('');
  const [totalServings, setTotalServing] = useState<string>('1'); // or '0' if you prefer a numeric default
  const [nutrients, setNutrients] = useState<Nutrients[]>();
  const [ingredients, setIngredients] = useState<PassioFoodItem[]>();

  // useEffect to initialize data
  useEffect(() => {
    async function init() {
      try {
        let collectNutrients: Nutrients[] = [];
        let collectFoodItem: PassioFoodItem[] = [];

        // Fetch attributes for each mock ingredient in parallel
        await Promise.all(
          mockIngredients?.map(async (item) => {
            // Get attributes for the PassioID
            const result = await PassioSDK.getAttributesForPassioID(item);

            // Skip if result is null
            if (result === null) {
              return;
            }

            // Process result for recipe
            if (result && result.recipe) {
              collectNutrients.push(
                ...(result.recipe, requireNutrientTypes)
              );
              collectFoodItem.push(...result.recipe?.foodItems);
            }

            // Process result for food item
            if (result.foodItem) {
              collectFoodItem.push(result.foodItem);
              collectNutrients.push(
                ...(result.foodItem, requireNutrientTypes)
              );
            }

            return result;
          })
        );

        // Merge collected nutrients and update state
        const allMergeNutrients = (collectNutrients);
        setNutrients(allMergeNutrients);
        setIngredients(collectFoodItem);
      } catch (error) {
        // Handle errors if needed
      }
    }

    // Call the init function when the component mounts
    init();
  }, []);

  // Return state variables and functions for the recipe editor
  return {
    totalServings,
    setRecipeName,
    recipeName,
    setTotalServing,
    nutrients,
    ingredients,
  };
};

To access the methods related to retrieving nutrients, please refer to the following this link. For information on the Nutrients class and NutrientsTypes, kindly explore the provided resource.

pageGetting nutrition value for a FoodItem

To access the methods for getNutrientsFromRecipe and mergeNutrients, please refer to the following below link. The details on these methods and their functionality can be found in the provided resource.

pageGetting nutrition value for a Recipe

RecipeEditorScreen

import React from 'react';
import {
  Dimensions,
  FlatList,
  ScrollView,
  StyleSheet,
  Text,
  TextInput,
  View,
} from 'react-native';
import RecipeMacroView from './views/RecipeMacroView';
import {
  PassioIconView,
  type PassioFoodItem,
  IconSize,
} from '@passiolife/nutritionai-react-native-sdk-v2';
import { useRecipeEditor } from './useRecipeEditor';
import { formatNutrient, type Nutrients } from '../../../utils/NutrientsUtils';

// Get the width of the window for styling purposes
const { width } = Dimensions.get('window');

// React component for the Recipe Editor screen
const RecipeEditorScreen = () => {
  // Destructure state and functions from useRecipeEditor hook
  const {
    recipeName,
    setRecipeName,
    nutrients,
    setTotalServing,
    totalServings,
    ingredients,
  } = useRecipeEditor();

  // Render individual ingredient in the FlatList
  const renderIngredients = ({ item }: { item: PassioFoodItem }) => {
    return (
      <View style={styles.ingredientContainer}>
        <PassioIconView
          style={styles.passioIcon}
          config={{
            passioID: item.passioID,
            iconSize: IconSize.PX180,
            passioIDEntityType: item.entityType,
          }}
        />
        <Text style={styles.itemIngredientFoodName}>{item.name}</Text>
      </View>
    );
  };

  // Render individual nutrient in the FlatList
  const renderNutrients = ({ item }: { item: Nutrients }) => {
    const nutrientsPerServing =
      (item.unitMass?.value ?? 0) /
      (totalServings.length > 0 ? Number(totalServings) ?? 0 : 1);
    return (
      <RecipeMacroView
        title={item.name}
        value={formatNutrient(nutrientsPerServing)}
      />
    );
  };

  // Return the JSX for the RecipeEditorScreen component
  return (
    <ScrollView style={styles.container}>
      {/* Input for Recipe Name */}
      <View style={styles.inputContainer}>
        <Text style={styles.inputTitle}>Recipe Name</Text>
        <TextInput
          onChangeText={(text) => setRecipeName(text)}
          placeholder="My Recipe"
          testID="testMyRecipe"
          style={styles.textInput}
          value={recipeName}
        />
      </View>

      {/* Input for Total Servings */}
      <View style={styles.inputContainer}>
        <Text style={styles.inputTitle}>
          <Text style={styles.inputTitle}>Total servings </Text>
          <Text style={styles.hintText}>(how many does it serve?)</Text>
        </Text>
        <TextInput
          value={totalServings?.toString()}
          testID={'testEditNumberOfServing'}
          onChangeText={(text) => setTotalServing(text)}
          placeholder="Number of servings"
          style={styles.textInput}
          keyboardType="numeric"
        />
      </View>

      {/* Section for Macros Per Serving */}
      <View style={styles.header}>
        <Text style={styles.titleText}>Macros Per Serving</Text>
      </View>
      <FlatList data={nutrients} renderItem={renderNutrients} />
      
      {/* Divider Line */}
      <View style={styles.line} />

      {/* Section for Ingredients */}
      <View style={styles.header}>
        <Text style={styles.titleText}>Ingredients</Text>
      </View>
      <FlatList
        data={ingredients}
        renderItem={renderIngredients}
        keyExtractor={(item) => item.passioID}
      />
    </ScrollView>
  );
};

// Styles for the RecipeEditorScreen component
const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  inputContainer: {
    marginTop: 24,
    marginLeft: 20,
  },
  // Styles for PassioIconView
  passioIcon: {
    height: 40,
    width: 40,
  },
  // Styles for individual ingredient container
  ingredientContainer: {
    flexDirection: 'row',
    alignItems: 'center',
    padding: 16,
    backgroundColor: 'white',
    marginHorizontal: 16,
    marginVertical: 8,
  },
  // Styles for TextInput
  textInput: {
    borderBottomWidth: 1,
    marginTop: 10,
    height: 40,
    width: width / 1.1 - 5,
    borderBottomColor: 'gray',
  },
  // Styles for ingredient text
  itemIngredientFoodName: {
    fontSize: 16,
    marginHorizontal: 8,
    textTransform: 'capitalize',
  },
  // Styles for section headers
  header: {
    marginHorizontal: 16,
    marginTop: 32,
  },
  // Styles for divider line
  line: {
    backgroundColor: 'gray',
    marginHorizontal: 16,
    marginTop: 16,
    height: 1,
  },
  // Styles for section titles
  titleText: {
    fontWeight: '600',
    fontSize: 15,
  },
  // Styles for input titles
  inputTitle: {
    fontWeight: '600',
    fontSize: 15,
    color: 'black',
  },
  // Styles for input hint text
  hintText: {
    fontWeight: '400',
    fontSize: 15,
    color: 'black',
  },
});

export default RecipeEditorScreen;

RecipeMacroView

import { StyleSheet, Text, View } from 'react-native';
import React from 'react';

const RecipeMacroView = ({
  title,
  value,
}: {
  title: string;
  value: number;
}) => {
  return (
    <View style={styles.ingredientStyle}>
      <Text style={styles.titleText}>{title}</Text>
      <Text style={styles.valueText}>{value.toFixed(0)}</Text>
    </View>
  );
};

const styles = StyleSheet.create({
  ingredientStyle: {
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'space-between',
    marginHorizontal: 16,
    marginTop: 12,
  },
  titleText: {
    fontWeight: '600',
    fontSize: 15,
    color: 'black',
  },
  valueText: {
    fontWeight: '400',
    fontSize: 15,
    color: 'black',
  },
});

export default RecipeMacroView;

Result

Last updated