Learn React by Building a Mortgage Calculator

React has quickly become one of the most popular libraries for building interactive user interfaces. Its component-based architecture, declarative syntax, and robust ecosystem make it a powerful tool for creating complex and data-driven applications. In this tutorial, we‘ll explore the core concepts of React development by building a real-world project – a mortgage calculator.

Why React for a Mortgage Calculator?

A mortgage calculator is a great example of a highly interactive, data-intensive application that can benefit from React‘s declarative approach to UI development. With React, we can break down the calculator into reusable components, each responsible for rendering a specific part of the interface and managing its own state and behavior.

For example, we might have separate components for:

  • The loan amount, interest rate, and term inputs
  • The calculated monthly payment display
  • The amortization schedule table or chart

By composing these modular components together, we can create a complex calculator interface while keeping our code organized and maintainable. React‘s efficient updates and virtual DOM also ensure that the UI remains fast and responsive as the user interacts with the calculator.

Key React Concepts

Before we dive into building the calculator, let‘s review some of the key React concepts we‘ll be using in this project:

Components

Components are the building blocks of a React application. They are reusable pieces of code that encapsulate both the structure (HTML) and behavior (JavaScript) of a part of the UI. React components can be either functional (stateless) or class-based (stateful).

JSX

JSX is a syntax extension for JavaScript that allows us to write HTML-like code in our React components. It gets compiled to regular JavaScript function calls that return objects representing the structure of our UI.

Props

Props (short for "properties") are a way to pass data from a parent component to a child component. They are read-only and allow us to configure a component‘s behavior and appearance.

State

State is data that can change over time and is managed internally by a component. When a component‘s state changes, React automatically re-renders the component to reflect the new state in the UI.

Hooks

Hooks are a new feature introduced in React 16.8 that allow us to add state and other React features to functional components. The most commonly used hooks are useState for managing state and useEffect for performing side effects like fetching data or subscribing to events.

Building the Calculator

Now that we have a basic understanding of React concepts, let‘s start building our mortgage calculator!

Project Setup

First, make sure you have Node.js installed, then create a new React project using Create React App:

npx create-react-app mortgage-calculator
cd mortgage-calculator
npm start

This will set up a basic React project and start the development server. In your project directory, replace the contents of src/App.js with:

import React from ‘react‘;

function App() {
  return (
    <div className="App">

    </div>
  );
}

export default App;

Creating the Input Components

Our mortgage calculator will accept three input values from the user:

  1. Loan amount
  2. Interest rate
  3. Loan term (in years)

Let‘s create a reusable input component that we can use for each of these values. Create a new file src/components/Input.js:

import React from ‘react‘;

function Input(props) {
  return (
    <div>
      <label>{props.label}</label>
      <input
        type="number" 
        value={props.value}
        onChange={props.onChange}
      />
    </div>
  );
}

export default Input;

This is a simple functional component that renders a label and input field. It expects three props:

  • label: The text to display as the input label
  • value: The current value of the input
  • onChange: A callback function to handle changes to the input value

By passing these props from the parent component, we can reuse this Input component for each of our calculator inputs.

Next, update App.js to import the Input component and add state and handlers for the input values:

import React, { useState } from ‘react‘;
import Input from ‘./components/Input‘;

function App() {
  const [loanAmount, setLoanAmount] = useState(100000);
  const [interestRate, setInterestRate] = useState(3.5);  
  const [loanTerm, setLoanTerm] = useState(30);

  function handleLoanAmountChange(event) {
    setLoanAmount(Number(event.target.value));
  }

  function handleInterestRateChange(event) {
    setInterestRate(Number(event.target.value));
  }

  function handleLoanTermChange(event) {
    setLoanTerm(Number(event.target.value));
  }

  return (
    <div className="App">

      <Input 
        label="Loan Amount"
        value={loanAmount}
        onChange={handleLoanAmountChange}  
      />
      <Input
        label="Interest Rate (%)"
        value={interestRate}
        onChange={handleInterestRateChange}
      /> 
      <Input
        label="Loan Term (years)"
        value={loanTerm}
        onChange={handleLoanTermChange}
      />
    </div>
  );
}

Here we‘re using the useState hook to create state variables and setter functions for each input value. The initial state values are set to some reasonable defaults for a typical mortgage.

We‘re also defining handler functions that get called whenever an input value changes. These handlers take the new value from the input‘s onChange event and update the corresponding state variable using the setter function.

Finally, we render three Input components in the App component‘s JSX, passing in the current state values and change handlers as props. This establishes the "single source of truth" pattern, where the parent App component always has the most up-to-date input values in its state.

Calculating the Monthly Payment

With the user‘s inputs in place, we can now calculate the monthly mortgage payment. The formula for this is:

P = L[c(1 + c)^n]/[(1 + c)^n - 1]

P = Monthly payment
L = Loan amount
c = Monthly interest rate 
n = Number of payments (loan term in months)

To use this formula in our React component, let‘s first create a utility function that performs the calculation. Create a new file src/utils/calculators.js:

function calculateMonthlyPayment(loanAmount, interestRate, loanTerm) {
  const monthlyRate = interestRate / 100 / 12;
  const numPayments = loanTerm * 12;

  const payment = 
    (loanAmount * monthlyRate) /
    (1 - Math.pow(1 + monthlyRate, -numPayments));

  return payment;
}

export { calculateMonthlyPayment };

Now we can import this function in App.js and use it to calculate the payment amount based on the current input state values:

import { calculateMonthlyPayment } from ‘./utils/calculators‘;

function App() {
  // ...

  const monthlyPayment = calculateMonthlyPayment(
    loanAmount,
    interestRate,
    loanTerm
  );

  return (
    <div className="App">
      { /* Inputs... */ }
      <h2>Monthly Payment: 
        {new Intl.NumberFormat(‘en-US‘, { 
          style: ‘currency‘, 
          currency: ‘USD‘ 
        }).format(monthlyPayment)}
      </h2>
    </div>
  );
}  

Here we‘re using the browser‘s built-in Intl.NumberFormat API to format the calculated payment as U.S. currency. Whenever any of the input values change, React will automatically recalculate monthlyPayment and update the UI.

Generating an Amortization Schedule

To give the user a more detailed breakdown of their mortgage payments over time, let‘s add an amortization schedule table to the calculator. This table will show how much of each monthly payment goes toward paying down the loan principal vs. interest charges.

We can generate the data for this table using a function like:

function generateAmortizationSchedule(
  loanAmount, 
  interestRate, 
  loanTerm,
  monthlyPayment
) {
  const schedule = [];
  const monthlyRate = interestRate / 100 / 12;
  let balance = loanAmount;

  for (let month = 1; month <= loanTerm * 12; month++) {
    const interestPayment = balance * monthlyRate;
    const principalPayment = monthlyPayment - interestPayment;

    balance -= principalPayment;

    schedule.push({
      month,
      payment: monthlyPayment,
      principal: principalPayment,
      interest: interestPayment,
      totalInterest: interestPayment + (schedule[month - 2]?.totalInterest || 0),
      balance
    });
  }

  return schedule;
}

export { generateAmortizationSchedule };

This function takes the loan details and monthly payment as inputs and returns an array of objects representing each monthly payment in the schedule. For each payment, we calculate the portion going toward interest and principal, update the remaining balance, and keep a running total of interest paid.

Back in App.js, we can generate the schedule data and pass it to a new AmortizationTable component for rendering:

import { 
  calculateMonthlyPayment, 
  generateAmortizationSchedule 
} from ‘./utils/calculators‘;
import AmortizationTable from ‘./components/AmortizationTable‘;

function App() {
  // ...

  const schedule = generateAmortizationSchedule(
    loanAmount,
    interestRate,
    loanTerm, 
    monthlyPayment
  );

  return (
    <div className="App">
      { /* Inputs and monthly payment... */ }

      <AmortizationTable data={schedule} />
    </div>
  );
}

The AmortizationTable component can then render this data as a table:

import React from ‘react‘;

function AmortizationTable({ data }) {
  const totalInterest = data[data.length - 1].totalInterest;

  return (
    <div>
      <table>
        <thead>
          <tr>
            <th>Month</th>
            <th>Payment</th>
            <th>Principal</th>
            <th>Interest</th>
            <th>Balance</th>
          </tr>
        </thead>
        <tbody>
          {data.map((row) => (
            <tr key={row.month}>
              <td>{row.month}</td>
              <td>${row.payment.toFixed(2)}</td>
              <td>${row.principal.toFixed(2)}</td>  
              <td>${row.interest.toFixed(2)}</td>
              <td>${row.balance.toFixed(2)}</td>
            </tr>
          ))}
        </tbody>
      </table>

      <p>
        Total interest paid: ${totalInterest.toFixed(2)}  
      </p>
    </div>
  );
}

Here we‘re using Array.map to render a table row for each payment object in the data array. We‘re also displaying the total interest paid over the life of the loan by accessing the last element‘s totalInterest property.

With this addition, our mortgage calculator is looking quite comprehensive! The user can input their loan details, see the calculated monthly payment, and examine a full amortization schedule to understand how their payments are allocated over time.

Additional Features and Improvements

There are many ways we could further enhance our mortgage calculator, such as:

  • Adding form validation and error handling for the inputs
  • Allowing users to specify additional costs like property taxes, insurance, and PMI
  • Providing an option to calculate the effects of making extra principal payments
  • Visualizing the amortization data in an interactive chart using a library like Chart.js
  • Refactoring the calculators and table into custom hooks for better reusability
  • Storing user data and preferences in local storage or a back-end database
  • Optimizing performance with memoization or code splitting

As you can see, even a relatively straightforward application like a mortgage calculator can provide ample opportunities to practice and expand your React skills!

React Best Practices and Performance

As you build more complex React projects, it‘s important to keep performance and maintainability in mind. Some best practices to follow include:

  • Keeping components small, focused, and reusable
  • Memoizing expensive calculations and avoiding unnecessary re-renders with React.memo, useMemo, and useCallback
  • Using the key prop when rendering lists of elements to help React efficiently update the DOM
  • Lazy loading non-critical components and assets with React.lazy and Suspense
  • Following a consistent project structure and naming conventions
  • Writing unit tests for your components and utility functions
  • Regularly profiling your application with the React DevTools to identify performance bottlenecks

By adhering to these guidelines and continuing to learn about React‘s ever-evolving ecosystem, you‘ll be well on your way to mastering the library and building robust, scalable applications.

Next Steps

Congratulations on completing this tutorial and building a fully featured mortgage calculator with React! I hope this project has given you a solid foundation in the core concepts and patterns of React development.

Your learning journey with React is just beginning. To further deepen your knowledge and skills, I recommend:

  • Exploring the official React documentation and tutorials
  • Building more projects to practice your new skills
  • Learning complementary libraries in the React ecosystem like React Router, Redux, and Jest
  • Attending React conferences, meetups, and workshops to learn from other developers
  • Contributing to open source React projects on GitHub
  • Staying up to date with the latest React releases and features

Remember, the best way to truly learn React is by getting your hands dirty and building real applications. Don‘t be afraid to experiment, make mistakes, and ask for help when you need it. The React community is incredibly welcoming and supportive, and there are countless resources available to help you grow as a developer.

If you have any questions or feedback about this tutorial, feel free to reach out. Happy coding, and best of luck on your React journey!

Similar Posts