I am using react-native-calendars
, aws-amplify
, moment
and uuid
to fetch data, display date data as dots marked on the calendar and then allow the user to open a modal with that dates items from the query in my React Native (Expo) app. I am getting an error, which isn't making much sense to me, I think it's the way I'm handling data manipulation/processing or property passing:
TypeError: undefined is not an object (evaluating 'item.createdAt')
This may be a long explanation, but I will explain to the best of my ability what's happening and where I think the error is coming from.
I have a query that returns a list of items, in this case they are notifications that have been sent to the user. I have this function which handles getting the data and structuring it so that I can pass the items to a sub-component. Here is how I am querying the info and how I'm storing the data.
CalendarCard.js (first half)
import React, { useEffect, useState } from "react";
import moment from 'moment';
import 'react-native-get-random-values';
import { v4 as uuidv4 } from 'uuid';
import { API } from "aws-amplify";
import { listUserNotifications } from '../../graphql/queries';
...
const CalendarCard = (
{
showCalendarInfoModal,
showDayModal,
setDayNotificationItems,
}) => {
const [markedDates,setMarkedDates] = useState();
const [notificationItems, setNotificationItems] = useState();
const currentMonth = moment().startOf('month').toISOString();
const now = new Date().toISOString();
useEffect(() => {
const recordInitNotifications = (notifications) => {
notifications.forEach((item,i) => {
let id = uuidv4().toString();
let createdAt = item.createdAt;
let notificationDate = moment(createdAt).format('YYYY-MM-DD');
let notificationId = notificationDate+id;
setMarkedDates(prev => ({
...prev,
[notificationDate]: {marked: true, dotColor: 'grey'}
}));
setNotificationItems(prev => ({
...prev,
[notificationId]: {[i]: item}
}));
});
};
const currentNotifications = async () => {
const initNotificationData = await API.graphql({
query: listUserNotifications,
variables: {
filter: {
createdAt: {
between: [currentMonth,now]
}
}
},
authMode: 'AMAZON_COGNITO_USER_POOLS'
});
const initNotifications = initNotificationData.data.listUserNotifications.items;
recordInitNotifications(initNotifications);
};
currentNotifications().catch(console.error);
}, []);
...
// continued below
The result of the above sets my notificationItems
variable as follows when I log it:
Object {
"2022-04-096d64e695-c377-4790-88bc-4de803f63028": Object {
"0": Object {
"PushToken": "ExponentPushToken[*********************]",
"StackName": "Everydaytest",
"StackSchedule": "{\"Everyday\":{\"dw\":[6,7,1,2,3,4,5],\"type\":\"DAY_WEEK\",\"startDate\":\"2022-04-09T15:00:00.000Z\"}}",
"StackScheduleName": "Everyday",
"UserID": "user",
"createdAt": "2022-04-09T15:00:22.504Z",
"id": "ee520f0a-cd93-47f9-954d-0c5f741daaa7",
"lastReceipt": "{\"stack\":\"patrick2022-04-09T14:52:37.519Z<139029b5-4402-4c08-b41f-b7653e64cb93>ba7cb97d-d6db-4086-82dd-b6a8c79712e5\",\"ticket\":{\"id\":\"e1ba6799-0a03-4126-a1fc-6fc4f39308d2\",\"status\":\"ok\"}}",
"lastReceiptCreated": "2022-04-09T15:00:22.504Z",
"owner": "user",
"updatedAt": "2022-04-09T15:00:22.504Z",
},
},
"2022-04-10213a8a85-7a0a-4e97-80c4-36711381f01f": Object {
"4": Object {
"PushToken": "ExponentPushToken[*********************]",
"StackName": "Pasta",
"StackSchedule": "{\"2 on, 1 off weekdays\":{\"dw\":[0,1,2,3],\"type\":\"DAY_WEEK\",\"startDate\":\"2022-03-13T11:00:00.000Z\"}}",
"StackScheduleName": "2 on, 1 off weekdays",
"UserID": "user",
"createdAt": "2022-04-10T11:00:23.093Z",
"id": "3fb75293-1afd-4a75-bdfd-b27961cd127e",
"lastReceipt": "{\"stack\":\"user2022-03-15T15:10:32.642Z<139029b5-4402-4c08-b41f-b7653e64cb93>d2cb5bf8-49ce-4d07-bbdc-aaa9418aae41\",\"ticket\":{\"id\":\"cb888fe2-a925-4571-9bc4-878ee585323b\",\"status\":\"ok\"}}",
"lastReceiptCreated": "2022-04-10T11:00:23.093Z",
"owner": "user",
"updatedAt": "2022-04-10T11:00:23.093Z",
},
},
"2022-04-1122b29a72-c30f-4a33-a164-0497b33122c0": Object {
"2": Object {
"PushToken": "ExponentPushToken[*********************]",
"StackName": "Pasta",
"StackSchedule": "{\"2 on, 1 off weekdays\":{\"dw\":[0,1,2,3],\"type\":\"DAY_WEEK\",\"startDate\":\"2022-03-13T11:00:00.000Z\"}}",
"StackScheduleName": "2 on, 1 off weekdays",
"UserID": "user",
"createdAt": "2022-04-11T11:00:22.341Z",
"id": "ff0d4f69-2d6b-4db8-90f6-db68c642042c",
"lastReceipt": "{\"stack\":\"user2022-03-15T15:10:32.642Z<139029b5-4402-4c08-b41f-b7653e64cb93>d2cb5bf8-49ce-4d07-bbdc-aaa9418aae41\",\"ticket\":{\"id\":\"dc451865-8b66-4088-93a5-8985d869eb1c\",\"status\":\"ok\"}}",
"lastReceiptCreated": "2022-04-11T11:00:22.341Z",
"owner": "user",
"updatedAt": "2022-04-11T11:00:22.341Z",
},
},
"2022-04-11cc0bdf71-c796-4252-b4aa-9f8ffd7e49a9": Object {
"3": Object {
"PushToken": "ExponentPushToken[*********************]",
"StackName": "Every other skip 4pm",
"StackSchedule": "{\"Every other day\":{\"skip_days\":2,\"type\":\"SKIP_DAYS\",\"startDate\":\"2022-04-11T19:46:34.080Z\"}}",
"StackScheduleName": "Every other day",
"UserID": "user",
"createdAt": "2022-04-11T20:00:22.549Z",
"id": "83cf53f2-9981-4eb1-bc8a-28e833de7e67",
"lastReceipt": "{\"stack\":\"user2022-04-11T19:47:42.994Z<139029b5-4402-4c08-b41f-b7653e64cb93>907e97a3-a438-49e7-bf5f-71ef8b23e7c4\",\"ticket\":{\"id\":\"0a4aa2dd-acb9-4cd2-89f4-5dec0b82337c\",\"status\":\"ok\"}}",
"lastReceiptCreated": "2022-04-11T20:00:22.549Z",
"owner": "user",
"updatedAt": "2022-04-11T20:00:22.549Z",
},
},
"2022-04-11dbf1fc4b-ca38-4ac5-b660-7b10195fc891": Object {
"1": Object {
"PushToken": "ExponentPushToken[*********************]",
"StackName": "Everydaytest",
"StackSchedule": "{\"Everyday\":{\"dw\":[6,7,1,2,3,4,5],\"type\":\"DAY_WEEK\",\"startDate\":\"2022-04-09T15:00:00.000Z\"}}",
"StackScheduleName": "Everyday",
"UserID": "user",
"createdAt": "2022-04-11T15:00:22.660Z",
"id": "597233b0-7327-4b13-923c-b3b9073c6747",
"lastReceipt": "{\"stack\":\"user2022-04-09T14:52:37.519Z<139029b5-4402-4c08-b41f-b7653e64cb93>ba7cb97d-d6db-4086-82dd-b6a8c79712e5\",\"ticket\":{\"id\":\"38eef4db-4914-4885-ae5e-7de509b7d424\",\"status\":\"ok\"}}",
"lastReceiptCreated": "2022-04-11T15:00:22.660Z",
"owner": "user",
"updatedAt": "2022-04-11T15:00:22.660Z",
},
},
}
While my markedDates
state looks like this:
Object {
"2022-04-09": Object {
"dotColor": "grey",
"marked": true,
},
"2022-04-10": Object {
"dotColor": "grey",
"marked": true,
},
"2022-04-11": Object {
"dotColor": "grey",
"marked": true,
},
}
Up to this point, everything works, but in the following code when I am handling the pressing of the day, and the subsequent opening of the modal, this is where the TypeError: undefined is not an object (evaluating 'item.createdAt')
occurs:
CalendarCard.js (second half)
// continuation of above code
...
function findValueByPrefix(obj,string) {
let items = [];
for (let property in obj) {
if (obj.hasOwnProperty(property) &&
property.toString().startsWith(string)) {
items.push(obj[property]);
};
};
return items;
};
const handleDayPress = (day) => {
const openModalWithData = (dayNotifications) => {
setDayNotificationItems(dayNotifications);
showDayModal();
};
const dayString = day.dateString;
let dayNotifications = findValueByPrefix(notificationItems,dayString);
openModalWithData(dayNotifications);
};
return (
<Card>
<Card.Title title='My History'/>
<Calendar
onMonthChange={month => handleMonthChange(month)}
markedDates={markedDates}
onDayPress={day => handleDayPress(day)}
/>
<Card.Content/>
</Card>
);
};
export default CalendarCard;
In the above code, setDayNotificationItems
and showDayModal
are passed into CalendarCard
from the parent component which can be seen here:
TrackerScreen.js
import React, { useState } from "react";
import { ScrollView, View } from "react-native";
import { Portal } from "react-native-paper";
import { SafeAreaView } from "react-native-safe-area-context";
import CalendarDayModal from "../components/modals/CalendarDayModal";
import CalendarCard from "../components/tracker/CalendarCard";
const TrackerScreen = () => {
const [dayNotificationItems, setDayNotificationItems] = useState();
const [dayModalVisible, setDayModalVisible] = useState(false);
const showDayModal = () => setDayModalVisible(true);
const hideDayModal = () => setDayModalVisible(false);
return (
<SafeAreaView>
<Portal>
<CalendarDayModal
dayModalVisible={dayModalVisible}
hideDayModal={hideDayModal}
dayNotificationItems={dayNotificationItems}
/>
</Portal>
<ScrollView>
<View>
<CalendarCard
showDayModal={showDayModal}
setDayNotificationItems={setDayNotificationItems}
/>
</View>
</ScrollView>
</SafeAreaView>
);
};
export default TrackerScreen;
From this, the data shown above is passed up from CalendarCard
to TrackerScreen
when a user presses the day. This then triggers the CalendarDayModal
to become visible and render a few components shown here:
CalendarDayModal.js
import React from "react";
import { View } from "react-native";
import { ScrollView } from "react-native-gesture-handler";
import { Caption, Headline, IconButton, Modal } from "react-native-paper";
import DayNotifications from "../tracker/DayNotifications";
const CalendarDayModal = (
{
dayModalVisible,
hideDayModal,
dayNotificationItems
}) => {
const RenderDayNotifications = () => {
if (dayNotificationItems.length > 0) {
return (
<View style={{flexDirection:'column',paddingVertical:5}}>
<Headline>Tracked Stacks</Headline>
<Caption>The things you tracked this day</Caption>
<DayNotifications
dayNotificationItems={dayNotificationItems}
/>
</View>
);
} else return(<View></View>);
};
return (
<Modal
visible={dayModalVisible}
onDismiss={hideDayModal}
contentContainerStyle={{
backgroundColor:'#D7FFFF',
width:'100%',
height: '100%',
alignSelf:'center'
}}
>
<IconButton
icon='check'
size={20}
style={{paddingTop: 10,position:'absolute',right:0,top:0}}
onPress={hideDayModal}
/>
<ScrollView style={{width: '95%',alignSelf:'center'}}>
<RenderDayNotifications/>
</ScrollView>
</Modal>
);
};
export default CalendarDayModal;
Here we see the end component where the TypeError: undefined is not an object (evaluating 'item.createdAt')
is manifesting itself, where the <DayNotifications/>
component is attempting to consume the data defined above. However, I believe part of the issue may be with my approach to filtering my data to select the items which match the day
object from onDayPress={day => handleDayPress(day)}
in the CalendarCard
component where I use this function:
function findValueByPrefix(obj,string) {
let items = [];
for (let property in obj) {
if (obj.hasOwnProperty(property) &&
property.toString().startsWith(string)) {
items.push(obj[property]);
};
};
return items;
};
const handleDayPress = (day) => {
const openModalWithData = (dayNotifications) => { setDayNotificationItems(dayNotifications);
showDayModal();
};
const dayString = day.dateString;
let dayNotifications = findValueByPrefix(notificationItems,dayString);
openModalWithData(dayNotifications);
};
Below is my DayNotifications
component, where the error is happening:
DayNotifications.js
import React, { useState } from "react";
import 'react-native-get-random-values';
import { v4 as uuidv4 } from 'uuid';
import moment from 'moment';
import { View } from "react-native";
import { Caption, Card, Subheading, Text } from "react-native-paper";
const DayNotifications = ({dayNotificationItems}) => {
const dayItems = [];
Object.entries(dayNotificationItems).forEach(([k,v]) => dayItems.push(v[`${k}`]));
const NotificationItemCard = ({item}) => {
let deliveryDate = moment(item.createdAt).format('dddd, MMMM Do YYYY');
// ^ ERROR TRIGGERED HERE
let deliveryTime = moment(item.createdAt).format('h:mma');
return(
<Card>
<Card.Title title={deliveryDate} subtitle={deliveryTime}/>
<View style={{
flexDirection: 'column',
width: '90%',
alignSelf:'center',
paddingBottom: 5
}}
>
<View style={{
flexDirection: 'col',
paddingVertical: 10
}}
>
<Subheading>{item.StackName}</Subheading>
<Caption>{item.StackScheduleName}</Caption>
</View>
</View>
</Card>
);
};
return (
<View style={{
width: '95%',
alignContent: 'center',
alignSelf:'center'
}}
>
{dayItems.map((item) =>
<NotificationItemCard key={uuidv4()} item={item}/>
)
}
</View>
);
};
export default DayNotifications;
So, why is it that my calendar is rendering the dots, which the dat is derived from the createdAt
field from the object resulting from the query, but after the transformations shown above, my DayNotifications
component says that item.createdAt
is undefined? Is there an error with the way I'm passing down this data or is the error with the way I am storing the data or more likely is it an issue with the way I'm filtering the data before passing it to DayNotifications
(which is what I think but have no idea what the issue is, yet)?
Solved, I was assuming that my findValueByPrefix
was going to return consistent keys, but the establishment of the key value in some nested objects (purely for unique key values purposes) was not in fact establishing keys in any order. Hence passing Object.entries(dayNotificationItems).forEach(([k,v]) => dayItems.push(v[
${k} ]));
would try to reference the nested object by a key that was not present in the object (it may have been in some instances just by coincidence, which is why this error was infuriating).
I elected to ignore the keys and just extract the values, since the keys were only there to ensure that objects which may be identical would not be overwritten or de-duped at any step. Here's all I had to do:
...
const dayItems = [];
Object.values(dayNotificationItems).forEach(item => {
Object.values(item).forEach(v => {
dayItems.push(v)
});
});
...
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.