This content originally appeared on DEV Community and was authored by mibii
Diving into different technologies can be both exciting and daunting. One effective way to master new skills is by working on real-world projects. Today, I’m sharing my experience developing a React application to fetch and display Bitcoin (BTC) unspent outputs. This journey covers various technologies, including React, Tailwind CSS, and working with APIs. Let’s break it down into key moments and learnings.
Setting Up the Project
Initializing the React App
First, set up your React project using Create React App. This command-line tool simplifies the process of setting up a new React project with a pre-configured development environment.
npx create-react-app btc-unspent-outputs
cd btc-unspent-outputs
Installing Tailwind CSS
Tailwind CSS is a utility-first CSS framework that helps you quickly style your application. It’s highly customizable and perfect for building modern web applications.
npm install -D tailwindcss
npx tailwindcss init
Configure Tailwind in tailwind.config.js
and include it in your CSS files.
// tailwind.config.js
module.exports = {
purge: ['./src/**/*.{js,jsx,ts,tsx}', './public/index.html'],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [],
}
/* index.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
Developing the Application
Managing State and Form Submission
We use React’s useState
to manage the state for unspent outputs, the BTC address input, loading state, and pagination. Handling form submission involves fetching unspent outputs for the provided BTC address.
import React, { useState } from 'react';
function App() {
const [outputs, setOutputs] = useState([]);
const [address, setAddress] = useState('');
const [isLoading, setIsLoading] = useState(false);
const [currentPage, setCurrentPage] = useState(1);
const [itemsPerPage] = useState(10);
const handleSubmit = (e) => {
e.preventDefault();
setIsLoading(true);
fetchUnspentOutputs(address)
.then(data => {
setOutputs(data);
setIsLoading(false);
setCurrentPage(1);
})
.catch(err => {
console.error('Error fetching unspent outputs:', err);
setOutputs([]);
setIsLoading(false);
});
};
const fetchUnspentOutputs = async (btcAddress) => {
const response = await fetch(`https://blockchain.info/unspent?active=${btcAddress}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
if (!data.unspent_outputs || !Array.isArray(data.unspent_outputs)) {
throw new Error('Invalid response format');
}
return data.unspent_outputs.map((output) => ({
txHash: output.tx_hash_big_endian,
outputIndex: output.tx_output_n,
value: output.value / 100000000,
confirmations: output.confirmations
}));
};
const formatBTC = (btc) => {
return btc.toLocaleString('en-US', {
minimumFractionDigits: 8,
maximumFractionDigits: 8
});
};
}
Validating BTC Address
To ensure the user inputs a valid BTC address, we use the bitcoin-address-validation
library. This helps prevent unnecessary API calls with invalid addresses.
npm install bitcoin-address-validation
import validate from 'bitcoin-address-validation';
// Inside handleSubmit function
if (!validate(address)) {
alert('Please enter a valid BTC address');
setIsLoading(false);
return;
}
Displaying Data in a Table
Once the data is fetched, we display it in a table. We ensure that each transaction hash is clickable, leading to a detailed view on a blockchain explorer.
// Inside the return statement
<div className="overflow-x-auto">
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50">
<tr>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Transaction Link</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Transaction Hash</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Output Index</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Value (BTC)</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Confirmations</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{outputs.map((output, index) => (
<tr key={index}>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 break-all">
<a href={`https://www.blockchain.com/explorer/transactions/btc/${output.txHash}`} target="_blank" rel="noopener noreferrer">
url_link_check_tx_hash
</a>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{output.txHash}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{output.outputIndex}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{formatBTC(output.value)}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{output.confirmations}
</td>
</tr>
))}
</tbody>
</table>
</div>
Pagination
To handle pagination, we slice the data array and display a subset of items based on the current page and items per page. We also add buttons to navigate between pages.
const indexOfLastItem = currentPage * itemsPerPage;
const indexOfFirstItem = indexOfLastItem - itemsPerPage;
const currentOutputs = outputs.slice(indexOfFirstItem, indexOfLastItem);
const paginate = (pageNumber) => setCurrentPage(pageNumber);
// Pagination buttons inside return statement
<div className="flex justify-center items-center mt-4 space-x-2">
<button onClick={() => paginate(currentPage - 1)} disabled={currentPage === 1} className="px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 disabled:bg-gray-200 disabled:text-gray-400"><</button>
<span className="text-sm text-gray-700">Page {currentPage}</span>
<button onClick={() => paginate(currentPage + 1)} disabled={indexOfLastItem >= outputs.length} className="px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 disabled:bg-gray-200 disabled:text-gray-400">></button>
</div>
Deployed - https://btcunspentoutputchecker.netlify.app/
Conclusion
Building this React application to fetch and display BTC unspent outputs has been an enlightening experience. It not only reinforces your React and Tailwind CSS skills but also teaches you how to work with external APIs and handle data efficiently. This project is a great example of how combining different technologies can result in a powerful and functional web application.
For beginners, the key takeaways are:
-
State Management: Using React’s
useState
to manage different states in the application. - Form Handling: Implementing form submission and data fetching with proper error handling.
- Data Display: Using tables to display fetched data and implementing pagination for better user experience.
- API Integration: Learning how to interact with external APIs and handle JSON responses.
By working through this project, you’ll gain a deeper understanding of these concepts and be better prepared for more advanced web development challenges. Happy coding!
This content originally appeared on DEV Community and was authored by mibii
mibii | Sciencx (2024-07-29T00:35:38+00:00) Building a React App to Fetch and Display BTC Unspent Outputs: A Beginner’s Guide. Retrieved from https://www.scien.cx/2024/07/29/building-a-react-app-to-fetch-and-display-btc-unspent-outputs-a-beginners-guide/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.