Reputation: 109
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
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>
);
}
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