Toan Lam
Toan Lam

Reputation: 109

Delete Modal. React Redux. Cannot read property '_id' of undefined

Trying to delete a transaction for an expense tracker. When the user click on the trash icon, a modal should appear. Instead Cannot read property '_id' of undefined is cast.

I'm guessing I need a useEffect hook with a match.params somewhere in there? Or would I need to list out transactions again on the modal?

data

 const transactions = [
  {
    name: "Rent",
    href: "#",
    category: "expense",
    amount: "-1000",
    currency: "USD",
    status: "processing",
    date: "July 1, 2020",
    datetime: "2020-07-11",
  },
  {
    name: "Google",
    href: "#",
    category: "income",
    amount: 5000,
    currency: "USD",
    status: "success",
    date: "July 18, 2020",
    datetime: "2020-07-18",
  },
  {
    name: "Facebook",
    href: "#",
    category: "income",
    amount: "15000",
    currency: "USD",
    status: "success",
    date: "July 18, 2020",
    datetime: "2020-07-18",
  },
  {
    name: "Payment to Molly Sanders",
    href: "#",
    category: "expense",
    amount: "-2000",
    currency: "USD",
    status: "success",
    date: "July 11, 2020",
    datetime: "2020-07-11",
  },
];

export default transactions;

app.js

 function App() {


return (
    <Router history={history}>
      <Route path='/' exact component={DashboardScreen} />
      <Route path='/transaction/add' exact component={AddTransactionModal} />
      <Route
        path='/transactions/delete/:id'
        exact
        component={DeleteTransactionModal}
      />
    </Router>
  );
}

transaction list:

import React, { useEffect } from "react";
import { Link } from "react-router-dom";
import { useDispatch, useSelector } from "react-redux";

const TransactionList = () => {
    const dispatch = useDispatch();
  
    const transactionList = useSelector((state) => state.transactionList);
    const { loading, error, transactions } = transactionList;
  
    useEffect(() => {
      dispatch(listTransactions());
    }, [dispatch]);
  
  
    return (
      <div className=''>
        <h2 className=''>
          Recent activity
        </h2>
      
        <div className=''>
          <nav
            className=''
            aria-label='Pagination'>
            <div className='f'>
              <a
                href='#'
                className=''>
                Previous
              </a>
              <a
                href='#'
                className=''>
                Next
              </a>
            </div>
          </nav>
        </div>
  
       
              <div className=''>
                <table className=''>
                  <thead>
                    <tr>
                      <th className=''>
                        Transaction
                      </th>
                      <th className=''>
                        Amount
                      </th>
                      <th className=''>
                        Action
                      </th>
                      <th className=''>
                        Date
                      </th>
                    </tr>
                  </thead>
                  <tbody className=''>
                    {transactions.map((transaction) => (
                      <tr key={transaction.id} className=''>
                        <td className=''>
                          <div
                            className="">
                            <a
                              href={transaction.href}
                              className=''>
                              <CashIcon
                                className=''
                                aria-hidden='true'
                              />
                              <p className=''>
                                {transaction.name}
                              </p>
                            </a>
                          </div>
                        </td>
                        <td className=''>
                          <span className=''>
                            {numberWithCommas(transaction.amount)}{" "}
                          </span>
                          {transaction.currency}
                        </td>
                        <td className=''>
                          <span className=' '>
                            <Link to={`/transactions/delete/${transaction.id}`}>
                              <TrashIcon
                                className=''
                                aria-hidden='true'
                              />{" "}
                            </Link>
                            <Link>
                              <PencilAltIcon
                                className=''
                                aria-hidden='true'
                              />
                            </Link>
                          </span>
                        </td>
  
                        <td className=''>
                          <time dateTime={transaction.datetime}>
                            {transaction.date}
                          </time>
                        </td>
                      </tr>
                    ))}
                  </tbody>
                </table>
              </div>
            </div>
   
    );
  };
  
  export default TransactionList;



 

delete transaction modal:

import React from "react";
import { useRef, useState, useEffect } from "react";


import { useParams } from "react-router-dom";
import { connect, useDispatch, useSelector } from "react-redux";
import { Redirect, Link } from "react-router-dom";
import {
  deleteTransaction,

} from "../../actions/transactionActions";

import DashboardScreen from "../../screens/DashboardScreen";
import StandardModal from "./StandardModal";

const DeleteTransactionModal = () => {
  const dispatch = useDispatch();
  const { id } = useParams();
  const [open, setOpen] = useState(true);
  const cancelButtonRef = useRef(null);


  if (!open) {
    return <Redirect to='/' />;
  }

  const actions = (
    <React.Fragment>
      <button
        type='button'
        onClick={() => dispatch(deleteTransaction(id))}
        className=''>
        Delete
      </button>
      <Link
        to='/'
        type='button'
        className=''
        onClick={() => setOpen(false)}
        ref={cancelButtonRef}>
        Cancel
      </Link>
    </React.Fragment>
  );

  return (
    <div>
      <DashboardScreen />
      console.log(transaction.id)
      <StandardModal
        title='Delete Transaction'
        content='Are you sure you want to delete this transaction?'
        actions={actions}
      />
    </div>
  );
};

export default connect(null, { deleteTransaction })(DeleteTransactionModal);

Upvotes: 1

Views: 243

Answers (1)

Drew Reese
Drew Reese

Reputation: 202836

Since DeleteTransactionModal is directly rendered by a route, access the transaction id from the route props that are passed as props.

const DeleteTransactionModal = ({
  deleteTransaction,
  match, // <-- destructure the match prop
}) => {
  const { id } = match.params; // <-- access the id param

  const actions = (
    <React.Fragment>
      <button
        type='button'
        onClick={() => deleteTransaction(id)} // <-- pass id to action
        className='modal-css'
      >
        Delete
      </button>
      <Link
        to='/'
        type='button'
        className='modal-css'
        onClick={() => setOpen(false)}
        ref={cancelButtonRef}
      >
        Cancel
      </Link>
    </React.Fragment>
  );


  return (
    <div>
      <DashboardScreen />
      <StandardModal
        title='Delete Transaction'
        content='Are you sure you want to delete this transaction?'
        actions={actions}
      />
    </div>
  );
}

export default connect(null, { deleteTransaction })(
  DeleteTransactionModal
);

I suggest converting over to React hooks over using the connect HOC.

import { useDispatch } from 'react-redux';
import { useParams } from 'react-router-dom';

const DeleteTransactionModal = () => {
  const dispatch = useDispatch();
  const { id } = useParams();

  const actions = (
    <React.Fragment>
      <button
        type='button'
        onClick={() => dispatch(deleteTransaction(id))}
        className='modal-css'
      >
        Delete
      </button>
      <Link
        to='/'
        type='button'
        className='modal-css'
        onClick={() => setOpen(false)}
        ref={cancelButtonRef}
      >
        Cancel
      </Link>
    </React.Fragment>
  );


  return (
    <div>
      <DashboardScreen />
      <StandardModal
        title='Delete Transaction'
        content='Are you sure you want to delete this transaction?'
        actions={actions}
      />
    </div>
  );
}

Route optimization suggestion

Since it seems you only want to render a single route at a time, render them into a Switch component and reorder the routes from most specific path to least specific path. This allows you to remove the exact prop.

<Router history={history}>
  <Switch>
    <Route path='/transactions/delete/:id' component={DeleteTransactionModal} />
    <Route path='/transaction/add' component={AddTransactionModal} />
    <Route path='/' component={DashboardScreen} />
  </Switch>
</Router>

Upvotes: 1

Related Questions