1

Newbie question, I am getting a TypeError: Cannot read properties of undefined (reading 'id') on the following code. Am I passing in props correctly? Why would this value be undefined? My getValues button returns an empty list with a length attribute of 0, so why is it that this is undefined when created and passed to child component?

InputScreen.js

import React, { useState } from 'react';
import { v4 as uuidv4 } from 'uuid';
import { View, StyleSheet, ScrollView, FlatList } from 'react-native';
import { Button, Caption } from 'react-native-paper';
import InputCard from '../components/InputCard';

const InputScreen = props => {
    const [formState, setFormState] = useState([]);
    
    const addInput = () => {      
        setFormState([
            ...formState,
            {
                id: formState.length,
                substance: "",
                amount: "",
                measure: ""
            },
        ]);
    };

    const handleInputChange = (index, newStateForIndex) => {
        let newFormState = [...formState];
        setFormState([
            ...newStateForIndex.slice(0,index),
            newStateForIndex,
            ...newStateForIndex.slice(index+1)
        ]);
    };
    
    const removeLastInput = () => {
        if (formState.length > 0) {
            const lastindex = formState.length - 1;
            setFormState(formState.filter((item, index) => index !== lastindex));  
        }
    };

    const getValues = () => {
        console.log('Form State: ',formState,'Length: ',formState.length);
    };

    return (
        <ScrollView>
            <View style={styles.col}>
                <View style={styles.row}>
                    <Caption>What substances are you checking out?</Caption>
                </View>
                <View style={styles.row}>
                    <View>
                        {formState.map((state, i) => 
                            <InputCard
                                key={uuidv4()}
                                formState={state[i]}
                                handleChange={handleInputChange}
                            />
                        )}
                    </View>
                </View>
                <View>
                    <View style={styles.col}>
                        <Button title='Add' onPress={addInput}>Add</Button>
                    </View>
                    <View style={styles.col}>
                        <Button title='Remove' onPress={removeLastInput}>Remove</Button>
                    </View>
                    <View style={styles.col}>
                        <Button title='GetVals' onPress={getValues}>Get Values</Button>
                    </View>
                </View>
            </View>
        </ScrollView>
    );
};

export default InputScreen;

InputCard.js

import React from "react";
import { View, StyleSheet } from 'react-native';
import { Caption, Card, TextInput } from "react-native-paper";

const InputCard = ({ formState, handleChange }) => {
    
    const id = formState.id; // <- Error occurs here
    
    return (
        <View>
            <Card>
                <Card.Content>
                    <Caption>Item {id}</Caption>
                    <View style={styles.row}>
                        <View style={styles.half}>
                            <TextInput
                                label="substance"
                                value={formState.substance}
                                onChangeText={(event) => {
                                    handleChange(
                                        id,
                                        {
                                            ...formState,
                                            substance: event.target.value
                                        });
                                }} // change needs to be made from here to parent
                                mode="outlined"
                                right={<TextInput.Icon name="pill" />}
                                style={styles.textfield}
                            />
                        </View>
                        <View style={styles.quarter}>
                            <TextInput
                                label="amount"
                                value={formState.amount}
                                onChangeText={(event) => {
                                    handleChange(
                                        id,
                                        {
                                            ...formState,
                                            substance: event.target.value
                                        });
                                }}
                                mode="outlined"
                                keyboardType="number-pad"
                            />
                        </View>
                        <View style={styles.quarterlast}>
                            <TextInput
                                label="measure"
                                value={formState.measure}
                                onChangeText={(event) => {
                                    handleChange(
                                        id,
                                        {
                                            ...formState,
                                            substance: event.target.value
                                        });
                                }}
                                mode="outlined"
                            />
                        </View>
                    </View>
                </Card.Content>
            </Card>
        </View>
    );
};

export default InputCard;

1 Answer 1

2

Your formState represents an array containing objects, so inside the loop, your first argument is an element of that array which is an object, you don't need to use an index for it.

<View>
  {formState.map((state, i) => 
     <InputCard
       key={uuidv4()}
       formState={state} // <- pass state instead of state[i]
       handleChange={handleInputChange}
      />
  )}
</View>

Not the answer you're looking for? Browse other questions tagged or ask your own question.