DEV Community

Cover image for Learn React By Building A React Finance Tracker.
Pauline Oraro
Pauline Oraro

Posted on • Updated on

Learn React By Building A React Finance Tracker.

In today's fast paced world managing your finances effectively is a necessity. Keeping a close eye on your income and expenses can help you make informed financial decisions, save for your goals and achieve financial stability. Fortunately, technology has made this task more accessible and user-friendly than ever before. In this blog post, we will embark on a journey to create a powerful and customizable financial tracking tool using react.

You can view the code and live demo.

This is what we will be building;

Financial Tracker

The user can;

  • Add income and expenses which have a name and a cost.

  • Remove/delete the transactions.

  • View the balance.

  • View their total income and expenses.

Table of contents

Prerequisites

Before we dive into the tutorial, make sure you have the following;

  • Node.js and npm- Ensure you have Node.js and npm(Node Package Manager) installed on your machine. You can download and install them from the official Node.js website.

  • Basic understanding of React- Familiarize yourself with the basics of React if you haven't already. You can check out the official react documentation for a quick start.

  • Code editor- You can use a code editor of your choice.

Setting up the project

Let's start by creating a new react application using create-react-app. Open your terminal and run the following command;

npx create-react-app finance-tracker
Enter fullscreen mode Exit fullscreen mode

This command will create a new React project named "finance-tracker". Once the project is created, navigate to the project directory;

cd finance-tracker
Enter fullscreen mode Exit fullscreen mode

Next we are going to install a package that allows us to generate ID's, run the following command;

npm i uuid
Enter fullscreen mode Exit fullscreen mode

Jump into the src folder and create a new folder called components. Within the components folder create other three folders.

  • moneyDetails- It shows the balance, income and expenses. It has two files; detail.js and details.css.

  • transactionItem - It contains the history of all transactions and has two files; item.js and item.css.

  • moneyManager - It is the financial tracker which allows users to add, view and delete transactions. It has two files; manager.js and manager.css.

Here is what the folder structure should look like;

finance tracker folder structure

Display the financial details

import './details.css';

//responsible for rendering the balance, income and expenses
const MoneyDetails = props =>{
    //represent the financial values for balance, income and expenses
    const {balanceAmount, incomeAmount, expensesAmount} = props
    return (
        <div className="money-details-container">
            <div className="balance-container">
                <img 
                src = "/balance-sheet.png"
                alt="balance"
                className='details-img'
                />
                <div>
                    <p className='details-text'>Your Balance</p>
                    <p className='details-money' testid="balanceAmount"> Ksh {balanceAmount}</p>
                </div>
            </div>
            <div className='income-container'>
                <img 
                src = "/growth.png"
                alt='income'
                className='details-img'
                />
                <div>
                    <p className='details-text'>Your Income</p>
                    <p className='details-money' testid="incomeAmount">Ksh {incomeAmount}</p>
                </div>
            </div>
            <div className='expenses-container'>
                <img 
                src='/expenses.png'
                alt='expenses'
                className='details-img'
                />
                <div>
                    <p className='details-text'>Your Expenses</p>
                    <p className='details-money' testid="expensesAmount">Ksh {expensesAmount}</p>
                </div>
            </div>
        </div>
    )
}


export default MoneyDetails;
Enter fullscreen mode Exit fullscreen mode

Let's head into the details.js file which is in the moneyDetails folder and type the above code.

The MoneyDetails component is responsible for rendering a summary of financial information which include balance, income and expenses with the provided values.

The component takes three props; balanceAmount, incomeAmount and expensesAmount. They represent the financial values for balance, income, expenses. Props are like arguments passed into react components. They are like function arguments in Javascript and attributes in HTML.

Display all transactions details

//display the history of the user's transactions
const TransactionItem = props => {
    const {transactionDetails, deleteTransaction} = props
    //represent the details of a transaction
    const {id, title, amount, type} = transactionDetails

    //it is called when the delete button is clicked
    const onDeleteTransaction = () => {
        deleteTransaction(id)
    }

    return (
        <li className='table-row'>
            <p className='transaction-text'>{title}</p>
            <p className='transaction-text'>Ksh {amount}</p>
            <p className='transaction-text'>{type}</p>
            <div className='delete-container'>
                <button className='delete-button' type='button' onClick={onDeleteTransaction} testid="delete">
                    <img 
                    className='delete-img'
                    src="/bin.png"
                    alt='delete'
                    />
                </button>
            </div>
        </li>
    )
}

export default TransactionItem;
Enter fullscreen mode Exit fullscreen mode

Let's head into the item.js file which is in the transactionItem folder and type the above code.

TransactionItem component is a functional component that displays all transaction details and allows the user to delete any transaction by clicking the delete button. It takes a single argument "props".

The transactionDetails and deleteTransaction are destructured from the "props" object. It allows the component to access these props easily.

The transactionDetails is further destructured to extract four properties; id, title, amount, type. These properties represent the details of a transactions.

The onDeleteTransaction function is called when the delete button is clicked and responsible for deleting the transaction. It takes no arguments and simply calls the deleteTransaction function with the id of the current transaction as an argument.

The return statement contains the JSX code that defines the structure of the transaction item. The paragraph displays the title, amount and type of the transaction. When the delete button is clicked it triggers the onDeleteTransaction.

The financial tracker

import { v4 } from 'uuid';
import TransactionItem from '../transactionItem/item';
import MoneyDetails from '../moneyDetails/details';
import React from 'react';

//an array that represents transaction types: income and expenses
const transactionTypeOptions = [
    {
        optionId: 'INCOME',
        displayText: 'Income',
    },
    {
        optionId: 'EXPENSES',
        displayText: 'Expenses',
    },
];

class MoneyManager extends React.Component {
    state = {
        transactionsList: [],
        titleInput: '',
        amountInput: '',
        optionId: transactionTypeOptions[0].optionId,
    }

    deleteTransaction = id => {
        const {transactionsList} = this.state;
        const updatedTransactionList = transactionsList.filter(
        eachTransaction => id !== eachTransaction.id,
    )
        this.setState({
        transactionsList: updatedTransactionList,
    })
    }


    onAddTransaction = event => {
            event.preventDefault()
            const {titleInput, amountInput, optionId} = this.state
            const typeOption = transactionTypeOptions.find(
        eachTransaction => eachTransaction.optionId === optionId,
    )
            const {displayText} = typeOption
            const newTransaction = {
        id: v4(),
        title: titleInput,
        amount: parseInt(amountInput),
        type: displayText,
    }
    this.setState(prevState => ({
        transactionsList: [...prevState.transactionsList, newTransaction],
        titleInput: '',
        amountInput: '',
        optionId: transactionTypeOptions[0].optionId,
    }))
    }

    onChangeOptionId = event => {
    this.setState({optionId: event.target.value})
    }

    onChangeAmountInput = event => {
    this.setState({amountInput: event.target.value})
    }

    onChangeTitleInput = event => {
    this.setState({titleInput: event.target.value})
    }

    getExpenses = () => {
        const {transactionsList} = this.state
        let expensesAmount = 0

        transactionsList.forEach(eachTransaction => {
        if (eachTransaction.type === transactionTypeOptions[1].displayText) {
        expensesAmount += eachTransaction.amount
        }
    })

    return expensesAmount;
    }

    getIncome = () => {
        const {transactionsList} = this.state
        let incomeAmount = 0
        transactionsList.forEach(eachTransaction => {
        if (eachTransaction.type === transactionTypeOptions[0].displayText) {
        incomeAmount += eachTransaction.amount;
        }
    })

    return incomeAmount;
    }

    getBalance = () => {
    const {transactionsList} = this.state
    let balanceAmount = 0
    let incomeAmount = 0
    let expensesAmount = 0

    transactionsList.forEach(eachTransaction => {
        if (eachTransaction.type === transactionTypeOptions[0].displayText) {
        incomeAmount += eachTransaction.amount
        } else {
        expensesAmount += eachTransaction.amount
        }
    })

    balanceAmount = incomeAmount - expensesAmount

    return balanceAmount
    }

    render() {
        const {titleInput, amountInput, optionId, transactionsList} = this.state
        const balanceAmount = this.getBalance()
        const incomeAmount = this.getIncome()
        const expensesAmount = this.getExpenses()

        return (
        <div className="app-container">
        <div className="responsive-container">
            <div className="header-container">
            <h1 className="heading">Finance Tracker &#128176;</h1>
            </div>
            <MoneyDetails
                balanceAmount={balanceAmount}
                incomeAmount={incomeAmount}
                expensesAmount={expensesAmount}
            />
            <div className="transaction-details">
            <form className="transaction-form" onSubmit={this.onAddTransaction}>
                <h1 className="transaction-header">Add Transaction</h1>
                <label className="input-label" htmlFor="title">
                    TITLE
                </label>
                <input
                    type="text"
                    id="title"
                    value={titleInput}
                    onChange={this.onChangeTitleInput}
                    className="input"
                />
                <label className="input-label" htmlFor="amount">
                    AMOUNT
                </label>
                <input
                    type="text"
                    id="amount"
                    className="input"
                    value={amountInput}
                    onChange={this.onChangeAmountInput}
                />
                <label className="input-label" htmlFor="select">
                    TYPE
                </label>
                <select
                    id="select"
                    className="input"
                    value={optionId}
                    onChange={this.onChangeOptionId}
                >
                    {transactionTypeOptions.map(eachOption => (
                    <option key={eachOption.optionId} value={eachOption.optionId}>
                        {eachOption.displayText}
                    </option>
                ))}
                </select>
                <button type="submit" className="button">ADD</button>
            </form>
            <div className='hTransactions'>
            <div className="history-transactions">
                <h1 className="transaction-header">History</h1>
                <div className="transactions-table-container">
                    <ul className="transactions-table">
                    <li className="table-header">
                        <p className="table-header-cell" >Title</p>
                        <p className="table-header-cell" >Amount</p>
                        <p className="table-header-cell" >Type</p>
                    </li>
                    {transactionsList.map(eachTransaction => (
                    <TransactionItem
                        key={eachTransaction.id}
                        transactionDetails={eachTransaction}
                        deleteTransaction={this.deleteTransaction}
                    />
                    ))}
                </ul>
                </div>
            </div>
            </div>

            </div>
        </div>
        </div>
    )
    }
}

export default MoneyManager;
Enter fullscreen mode Exit fullscreen mode

Let's head into the manager.js file which is in the moneyManager folder and type the above code.

The MoneyManager component serves as a financial tracker which allows users to add, view and delete transactions, categorize them as income or expenses.

The transactionTypeOptions is an array that contains two objects representing transaction types which are income and expenses. Each object has an optionId and a displayText.

MoneyManager is a class component and it contains;

state- it keeps track of;
- transactionsList - an array to store individual transactions.
- titleInput - a string to represent the title of a new transaction.
- amountInput - a string to represent the amount of a new transaction.
- optionId - the selected transaction type from the dropdown menu. It is initially set to the first option from transactionTypeOptions.

deleteTransaction- It takes an id as an argument and is responsible for removing a transaction from the transactionsList array. It uses the filter method to create a new array that contains transactions where the id does not match the id provided as an argument to the deleteTransaction. The state is then updated to reflect the updated list of transactions.

onAddTransaction - It is called when the user submits the transaction form. It prevents the default form submission extract the titleInput, amountInput and optionId from the component's state. It finds the corresponding transaction type either(income or expenses) from the transactionTypeOptions array. It creates a new transaction. It updates the component state, appends the newTransaction to the existing transactionList.

onChangeOptionId, onChangeAmountInput and onChangeTitleInput are used to handle changes in the input fields and the selection of transaction type. They update the corresponding fields in the component's state.

getExpenses calculates the total expenses by iterating through the transactionsList and summing up the amounts of transactions with the expense type.

getIncome calculates the total income by iterating through the transactionsList and summing up the amounts of transactions with the income type.

getBalance calculates the balance by subtracting total expenses from total income.

You can style your application with my css styling or you can customize your own styling. You can check out the code and live demo.

Top comments (0)