0

When I call on getFormattedDate:

export function getFormattedDate(date) {
    // return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate() +1}`
    return date.toISOString().slice(0, 10)
}

In this line:

<Text style={styles.textBase}>{getFormattedDate(date)}</Text>

I get "TypeError: date.toISOString is not a function (it is undefined)"

The data is now coming from mongodb instead of a dummy_expenses array and I can't figure out why getFormatted date does not recognize it as a date. From my research, this error happens because the piece of data being passed to it is not of type 'date'. I console logged the data before and after I changed where the data is coming from and both are date types. If you need any more information about this please let me know.

Before changes:

 LOG  Expense Item Shoes 2023-07-29T00:00:00.000Z

After changes:

 LOG  Expense Item EHDN 2023-08-09T00:00:00.000Z

The data is coming from expense-context.js. Here is the file before expense-context was connected to mongodb:

import { createContext, useReducer } from 'react'

const DUMMY_EXPENSES = [
    {
        id: 'e1',
        description: 'Shoes',
        amount: 59.99,
        date: new Date('2023-7-29'),
    }
    
]

export const ExpensesContext = createContext({
    expenses: [],
    addExpense: ({description, amount, date}) => { },
    deleteExpense: (id) => { },
    updateExpense: (id, { description, amount, date }) => { },
})

function expensesReducer(state, action) {
    switch (action.type) {
        case 'ADD':
            const id = new Date().toString() + Math.random().toString()
            return [{...action.payload, id: id},...state]
        case 'UPDATE':
            const updatableExpenseIndex = state.findIndex((expense) => expense.id === action.payload.id)
            const updatableExpense = state[updatableExpenseIndex]
            const updatedItem = { ...updatableExpense, ...action.payload.data }
            const updatedExpenses = [...state]
            updatedExpenses[updatableExpenseIndex] = updatedItem
            return updatedExpenses
        case 'DELETE':
            return state.filter((expense) => expense.id !== action.payload )
        default:
            return state
    }
}

function ExpensesContextProvider({ children }) {
    const [expensesState, dispatch] = useReducer(expensesReducer, DUMMY_EXPENSES)

    function addExpense(expenseData) {
        dispatch({ type: 'ADD', payload: expenseData })
    }

    function deleteExpense(id) {
        dispatch({ type: 'DELETE', payload: id })
    }

    function updateExpense(id, expenseData) {
        dispatch({ type: 'UPDATE', payload: { id: id, data: expenseData } })
    }
    
    const value = {
        expenses: expensesState,
        addExpense: addExpense,
        deleteExpense: deleteExpense,
        updateExpense: updateExpense 
    }
    
    return (
        <ExpensesContext.Provider value={value}>{children}</ExpensesContext.Provider>
    )
}

export default ExpensesContextProvider

Here is expense-context.js after I connected it to mongodb:

import { createContext, useReducer } from 'react';
import useFetchExpenses from '../hooks/useFetchExpenses'; // Import the custom hook
import axios from 'axios';

// ...
export const ExpensesContext = createContext({
    expenses: [],
    addExpense: ({ description, amount, date }) => { },
    setExpenses: (expenses) => {},
    deleteExpense: (id) => { },
    updateExpense: (id, { description, amount, date }) => {},
})

function expensesReducer(state, action) {
    switch (action.type) {
        case 'ADD':
            const id = new Date().toString() + Math.random().toString();
            return [{ ...action.payload, id: id }, ...state];
        case 'UPDATE':
            const updatableExpenseIndex = state.findIndex(
                (expense) => expense.id === action.payload.id
            );
            const updatableExpense = state[updatableExpenseIndex];
            const updatedItem = { ...updatableExpense, ...action.payload.data };
            const updatedExpenses = [...state];
            updatedExpenses[updatableExpenseIndex] = updatedItem;
            return updatedExpenses;
        case 'DELETE':
            return state.filter((expense) => expense.id !== action.payload);
        case 'SET_EXPENSES':
            // setExpenses()
            return action.payload
        default:
            return state;
    }
}

function ExpensesContextProvider({ children }) {
    const [expensesState, dispatch] = useReducer(expensesReducer, []);

    // Fetch expenses using the custom hook
    const { expenses, loading, error } = useFetchExpenses();
    console.log("useReducer data", expenses)

    // Rest of your CRUD functions (addExpense, deleteExpense, updateExpense)
    // Create: Add a function to create a new expense document in the database.
    async function addExpense(expenseData) {
        try {
            const response = await axios.post('http://localhost:8082/expense', expenseData);
            dispatch({ type: 'ADD', payload: response.data });
        } catch (error) {
            console.error('Error adding expense:', error);
        }
    }

    async function setExpenses() {
        try {
            const response = await axios.get('http://localhost:8082/expenses');
            // const expensesWithValidDates = response.data.map((expense) => ({
                // ...expense,
                // date: new Date(expense.date), // Convert date string to Date object
            // }));
            dispatch({ type: 'SET_EXPENSES', payload: response.data });
        } catch (error) {
            console.error('Error fetching expenses:', error);
        }
    }

    async function updateExpense(id, expenseData) {
        try {
            const response = await axios.put(`http://localhost:8082/expenses/${id}`, expenseData);
            dispatch({ type: 'UPDATE', payload: { id: id, data: response.data } });
        } catch (error) {
            console.error('Error updating expense:', error);
        }
    }

    async function deleteExpense(id) {
        try {
            await axios.delete(`http://localhost:8082/expenses/${id}`);
            dispatch({ type: 'DELETE', payload: id });
        } catch (error) {
            console.error('Error deleting expense:', error);
        }
    }

    const value = {
        expenses: expensesState, // Change to use expenses from the hook if needed
        addExpense: addExpense,
        deleteExpense: deleteExpense,
        updateExpense: updateExpense,
        setExpenses: expensesState, // Include the fetch function in the context if needed
    };

    return (
        <ExpensesContext.Provider value={value}>
            {children}
        </ExpensesContext.Provider>
    );
}

export default ExpensesContextProvider;
codereyes
  • 47
  • 9
  • My guess is that the date you get from backend is of type string. You can use Date constructor `new Date(date)` and you can chain on any methods. Also, make sure that you only call `getFormattedDate` function when you have a date string. You can have a ternary `{date ? getFormattedDate(date) : null}` – Inder Aug 11 '23 at 18:35
  • *I console logged the data before and after I changed where the data is coming from and both are date types*. How did you do that? Did you check with `instanceof`? – DallogFheir Aug 11 '23 at 18:38
  • After checking with instanceOf, turns out ‘date’ was a string. Thanks, @DallogFheir. I converted the date String to a Date and passed that const to getFormattedDate. Thanks Inder const myDate = new Date(date); ~~~~ {getFormattedDate(myDate)} – codereyes Aug 12 '23 at 16:51

1 Answers1

0

After checking with instanceOf, turns out ‘date’ was a string. I converted the date String to a Date and passed that const to getFormattedDate.

function ExpenseItem({ id, description, amount, date }) {
    const navigation = useNavigation()
    // Convert date string to Date object
    const myDate = new Date(date);

then passed myDate to getFormattedDate(myDate)

<Text style={styles.textBase}>{getFormattedDate(myDate)}</Text>
codereyes
  • 47
  • 9