Le Moi
Le Moi

Reputation: 1025

How to wire up deleteOne functionality in a React/Redux & MongoDB App

I have a React/Redux app that lists transactions, with data stored in a MongoDB collection. I have CRUD operations set up and can add and edit transactions, but I am struggling a bit with the delete functionality.

I have a delete endpoint created in Express.js:

app.delete('/api/transactions/:id', function(req, res) {
    Transaction.remove({
        _id: req.params.id
    }, function(err, transaction) {
        if (err)
            res.send(err);
        res.json({ message: 'Transaction deleted!' })
    });
});

In my Redux action creators I have the following:

export function deleteTransaction(id) {
    const request = axios.delete(`/api/transactions/${id}`);

    return {
        type: DELETE_TRANSACTION,
        payload: request
    }
}

I am fetching transactions through Redux in the componentWillMount() lifecycle method, and then mapping over the results to return a chunk of JSXL

{this.props.transactions.map(
    (transaction, i) => 
        <Transaction {...this.props} key={i} transaction={transaction} />
)}

I thought I could just create a method like the following:

handleDelete(id) {
    this.props.deleteTransaction(id)
        .then(() => {
            this.context.router.push('/');
        });
}

Then call this method onClick, and pass in the MongoDB Document _id from props, but in hooking this up and tinkering, I was getting errors stating cannot call deleteTransaction of undefined then other times it was deleting all collections... all sorts of weird errors that was messing up other areas of the application, so I have reverted and come seeking help from you React/Redux wizards :)

Can anyone point me in the right direction?

Update

I was calling handleDelete() and passing the ID from Transaction.js:

import React, { Component } from 'react';
import { Link } from 'react-router';
import numeral from 'numeral';
import moment from 'moment';

import TransactionEdit from './TransactionEdit';

export default class Transaction extends Component {

    render() {

        const { transaction } = this.props;

        return (
            <tr>
                <td>{transaction.name}</td>
                <td>{moment(transaction.date).format('Do MMM YYYY')}</td>
                <td>{`£${numeral(transaction.amount).format('£ 0,0[.]00')}`}</td>
                <td><Link to={`/transaction/edit/${transaction._id}`} className="button">Edit</Link></td>
                <td><button onClick={this.props.handleDelete(transaction._id)}className="button">Delete</button></td>
            </tr>
        );      
    }
}

For clarity, this is the entire Transactions.js file:

import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as actionCreators from '../actions/actionCreators';
import { Link } from 'react-router';

import Transaction from './Transaction';

class Transactions extends Component {
    componentWillMount() {
        this.props.fetchTransactions();
    }

    handleDelete(id) {
        this.props.deleteTransaction(id)
            .then(() => {
                this.context.router.push('/');
            });
    }

    static contextTypes = {
        router: PropTypes.object
    }

    render() {

        const { transactions } = this.props;

        if (!transactions) {
            return (
                    <div>
                        <p>Loading...</p>
                    </div>
            )
        }


        return (
            <section>
                <h2>Transactions <Link className="actionlink" to="/transactions/add">Add</Link></h2>
                <table className="financials -transactions">
                    <thead>
                        <tr>
                            <th>Name</th>
                            <th>Date</th>
                            <th className="activefilter">Amount</th>
                            <th className="actions">&nbsp;</th>
                            <th className="actions">&nbsp;</th>
                        </tr>
                    </thead>
                    <tbody>
                    {this.props.transactions.map(
                        (transaction, i) => 
                            <Transaction {...this.props} key={i} transaction={transaction} />
                    )}
                    </tbody>
                </table>
            </section>
         );
    }
}

function mapStateToProps(state) {
    return { 
        transactions: state.transactions.all 
    };
}

function mapDispatchToProps(dispatch) {
    return bindActionCreators(actionCreators, dispatch);
}

export default connect(mapStateToProps, mapDispatchToProps)(Transactions);

Thanks :)

Upvotes: 0

Views: 995

Answers (1)

Eric Tran
Eric Tran

Reputation: 126

Id is undefined in your handleDelete helper function because I believe you need to bind this instance of the handleDelete function in order to pass the id. In your onClick method try onClick={this.props.handleDelete.bind(this, transaction._id)}

Upvotes: 1

Related Questions