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:
- Loan amount
- Interest rate
- 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 labelvalue
: The current value of the inputonChange
: 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
, anduseCallback
- 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!