本指南的目的是为读者提供有关创建NFT档案的说明,重点关注任务所需的关键技术框架和工具。通过使用 Chainbase,一个提供用户友好API端点用于NFT管理的平台,用户可以全面了解如何有效处理NFTs。
原文作者:masterdai
非同质化代币(NFTs)在数字艺术和收藏品市场引起了革命,标志着我们对数字领域内所有权和价值的感知发生了重大变化。
本指南的目的是为读者提供有关创建NFT档案的说明,重点关注任务所需的关键技术框架和工具。通过使用Chainbase,一个提供用户友好API端点用于NFT管理的平台,用户可以全面了解如何有效处理NFTs。
在这个前端demo中,我们需要利用两个Chainbase API,并将它们结合起来实现相对复杂的功能。例如,使用ENS地址进行NFT查询,以及通过连接您的钱包地址并输入NFT合约地址进行精确搜索。此外,可以通过调整滚动条来探索多链NFTs。
https://nftdemo-lime.vercel.app/
技术要求: 要创建一个NFT档案,您将需要一些关键的工具和技术。首要要求是对React的深刻理解,因为它将是我们用于构建应用程序前端的主要库。此外,对Axios和Fetch的了解对于处理HTTP请求至关重要。这些库将使我们能够有效地与API进行交互。
设置环境:
npx create-react-app my-nft-profile
来完成此操作。npm install axios
安装Axios。Fetch内置在现代浏览器中,因此如果使用Chrome或Firefox等浏览器,则无需额外安装。有了这些工具和技术的设置,您就可以开始构建您的NFT档案了。
在本节中,我们将详细介绍将编码过程分解为NFT演示的各个部分的步骤,包括每个部分的代码示例和解释。
BlockchainSelector
组件useChainId
: 管理所选区块链网络的状态。useChainId
钩子进行网络选择,并在更改时更新所选的链ID。import React, { useState, useEffect, useMemo } from 'react';
import PropTypes from 'prop-types';
import styles from '../styles/Home.module.css';
const capitalizeFirstLetter = (string) => {
return string.charAt(0).toUpperCase() + string.slice(1);
}
const useChainId = (initialNetwork) => {
const networkToChainId = useMemo(() => ({
ethereum: 1,
polygon: 137,
bsc: 56,
avalanche: 43114,
arbitrum: 42161,
optimism: 10,
base: 8453,
zksync: 324,
}), []);
const [selectedNetwork, setSelectedNetwork] = useState(initialNetwork);
const selectedChainId = networkToChainId[selectedNetwork];
return {
selectedNetwork,
setSelectedNetwork,
selectedChainId,
networkToChainId
};
};
const BlockchainSelector = ({ setSelectedChainId }) => {
const { selectedNetwork, setSelectedNetwork, selectedChainId, networkToChainId } = useChainId('ethereum');
useEffect(() => {
setSelectedChainId(selectedChainId);
}, [selectedChainId]);
const handleChange = (event) => {
setSelectedNetwork(event.target.value);
};
return (
<div className={styles.blockchainSelectorContainer}>
<label htmlFor="blockchainSelector" className={styles.blockchainSelectorLabel}>
Select a Blockchain Network:
</label>
<select id="blockchainSelector" value={selectedNetwork} onChange={handleChange}>
{Object.keys(networkToChainId).map((network) => (
<option key={network} value={network}>
{capitalizeFirstLetter(network)}
</option>
))}
</select>
</div>
);
};
BlockchainSelector.propTypes = {
setSelectedChainId: PropTypes.func.isRequired,
};
export default BlockchainSelector;
NftCard
组件convertToHttpUrl
:将IPFS URL转换为显示。handleImageError
:处理图像加载期间的错误。import React, { useState, useEffect } from 'react';
const convertToHttpUrl = (url) => {
if (typeof url === 'string' && url.startsWith('ipfs://')) {
return `https://cloudflare-ipfs.com/ipfs/${url.split('ipfs://')[1]}`;
}
return url;
};
const handleImageError = (e,setImageError) => {
const ipfsPattern = /ipfs\/(Qm[1-9A-Za-z]{44}\/?.*)/;
const match = e.target.src.match(ipfsPattern);
if (match) {
e.target.src = `https://cloudflare-ipfs.com/ipfs/${match[1]}`;
}
else {
setImageError(true);
}
};
const NftCard = ({ nft }) => {
const maxTokenIdLength = 10;
const truncatedTokenId = nft.token_id.length > maxTokenIdLength ? nft.token_id.substring(0, maxTokenIdLength) + '...' : nft.token_id;
const imageUri = nft.image_uri || (nft.metadata ? nft.metadata.image : '');
const imageUrl = convertToHttpUrl(imageUri);
const [isVideo, setIsVideo] = useState(false);
const [imageError, setImageError] = useState(false);
//console.log('Image URL:', imageUrl);
useEffect(() => {
fetch(imageUrl)
.then((response) => {
if (!response.ok) {
throw new Error('Failed to fetch image');
}
const contentType = response.headers.get('Content-Type');
if (contentType && contentType.startsWith('video')) {
setIsVideo(true);
}
})
.catch((error) => {
// This is where errors can be logged or default images can be set, but it will not cause unhandled runtime errors.
console.error('Error fetching image:', error);
});
}, [imageUrl]);
return (
<div style={{ width: '25%', padding: '10px', border: '1px solid #ccc' }}>
{isVideo ? (
<video src={imageUrl} controls style={{ width: '100%' }} />
): imageError ? (
<div>NFT not shown</div> // If the image fails to load, display this message.
)
: (
<img src={imageUrl} alt={nft.name} style={{ width: '100%' }} onError={(e) => handleImageError(e, setImageError)} />
)}
<h3>{nft.name}</h3>
<p><strong>Symbol:</strong> {nft.symbol}</p>
<p><strong>Token ID:</strong> <span title={nft.token_id}>{truncatedTokenId}</span></p>
<p><strong>Contract Address:</strong> {nft.contract_address}</p>
</div>
);
};
export default NftCard;
SearchBar
组件import React, { useState } from 'react';
const SearchBar = ({ onSearch }) => {
const [inputAddress, setInputAddress] = useState(''); // 用户手动输入的地址
const [connectedAddress, setConnectedAddress] = useState(''); // 从MetaMask连接的地址
const [contractAddress, setContractAddress] = useState('');
const connectMetaMask = async () => {
if (!window.ethereum) {
alert('Please install MetaMask first.');
return;
}
let accounts;
try {
accounts = await window.ethereum.request({ method: 'eth_requestAccounts' });
setConnectedAddress(accounts[0]);
setInputAddress(''); // 连接后清除地址
} catch (error) {
alert('You rejected the request.');
}
};
const disconnectWallet = () => {
setConnectedAddress(''); // 清除连接的MetaMask地址
setInputAddress(''); // 同时清除输入地址
};
const handleSearch = () => {
const addressToSearch = connectedAddress || inputAddress;
onSearch(addressToSearch, contractAddress);
};
return (
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
{connectedAddress ? (
<>
<span>Connected Address: {connectedAddress}</span>
<button onClick={disconnectWallet}>Disconnect Wallet</button>
</>
) : (
<>
<button onClick={connectMetaMask}>Connect MetaMask</button>
<input
type="text"
placeholder='Your ETH address or ENS name'
value={inputAddress}
onChange={(e) => setInputAddress(e.target.value)}
style={{ margin: '10px 0', width: '300px' }}
/>
</>
)}
<input
type="text"
placeholder='NFT Contract address (optional)'
value={contractAddress}
onChange={(e) => setContractAddress(e.target.value)}
style={{ margin: '10px 0', width: '300px' }}
/>
<button onClick={handleSearch}>Search</button>
</div>
);
};
export default SearchBar;
Home
组件import React, { useState } from 'react';
import axios from 'axios';
import SearchBar from '../components/SearchBar';
import NftCard from '../components/NftCard';
import BlockchainSelector from '../components/BlockchainSelector';
const Home = () => {
const [nftData, setNftData] = useState(null);
const [selectedChainId, setSelectedChainId] = useState('');
const handleSearch = async (address, contractAddress) => {
setNftData(null);
try {
const response = await axios.get(`/api/chainbaseApi?address=${address}&contract_address=${contractAddress}&chain_id=${selectedChainId}`);
setNftData(response.data.data);
} catch (error) {
console.error(error);
}
};
return (
<div style={{ textAlign: 'center' }}>
<img src="/logo.png" alt="Chainbase Logo" style={{ marginTop: '20px', width: '300px', height: '50px' }} />
<BlockchainSelector setSelectedChainId={setSelectedChainId} />
<SearchBar onSearch={handleSearch} />
{nftData && nftData.length > 0 ? (
<div>
<h2>NFTs for Address: {nftData[0].owner}</h2>
<div style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'center' }}>
{nftData.map((nft) => (
<NftCard key={`${nft.contract_address}-${nft.token_id}`} nft={nft} />
))}
</div>
</div>
) : (
<div></div>
)}
</div>
);
};
export default Home;
该 NFT 演示中的每个组件都旨在处理特定功能,确保明确的关注点分离。这种模块化方法有助于有效地维护和扩展应用程序。
在这一步中,我们将深入探讨将Chainbase API集成到我们的NFT演示中的关键方面,特别关注使用ENS API解析以太坊地址的用途。
export default async function handler(req, res) {
// Logging and CORS setup
}
if (address.endsWith('.eth')) {
// Fetching from Chainbase ENS API
// Convert ENS to Ethereum address
}
构建Chainbase API请求URL:
chain_id
,address
和contract_address
等参数到基本URL。const baseUrl = `https://api.chainbase.online/v1/account/nfts?chain_id=${chainId}`;
let targetUrl = `${baseUrl}&address=${address}&limit=100`;
if (query.contract_address) {
targetUrl += `&contract_address=${query.contract_address}`;
}
从Chainbase API获取数据:
fetch
函数通过构建的URL向Chainbase API发出GET请求。const backendResponse = await fetch(targetUrl, {
headers: {
accept: 'application/json',
'x-api-key': process.env.CHAINBASE_API_KEY,
},
});
// Processing and sending the response to the front-end
try {
// Chainbase API request and response handling
} catch (error) {
res.status(500).json({ error: 'Error' });
}
这个处理程序函数对于NFT演示的功能至关重要,因为它无缝地集成了ENS名称解析和从Chainbase API检索NFT数据。理解这个函数提供了一个全面的视图,说明了如何实现后端逻辑以支持Web应用程序的前端功能。
在部署NFT个人资料应用程序之前,在本地开发环境中进行彻底的测试非常重要。
npm run dev
。此命令启动开发服务器,使您可以在本地访问应用程序。http://localhost:3000
或终端中提供的类似URL上可访问。一旦您的应用程序经过测试并准备就绪,您可以将其部署到诸如Vercel之类的云平台以供公共访问。
npm install -g vercel
或按照Vercel文档中的说明安装Vercel CLI。package.json
文件正确设置了启动脚本。vercel
以启动部署。Vercel CLI将指导您完成此过程。部署到Vercel等平台提供了易用性、可扩展性和与各种前端框架的集成的优势。记得保持API密钥和敏感数据的安全,并避免在客户端代码中公开它们。
在本教程中,我们介绍了使用现代Web技术创建NFT个人资料的关键步骤。我们首先通过使用React和Axios等工具设置了我们的环境。然后,我们深入探讨了编码过程,其中我们开发了几个组件,如BlockchainSelector
,NftCard
和SearchBar
,每个在我们的应用程序中扮演着重要的角色。
我们还探讨了如何集成Chainbase API以获取NFT数据,包括处理以太坊名称服务(ENS)地址。强调了测试和部署的重要性,确保我们的应用程序是健壮且可访问的。
NFT个人资料的创建不仅突显了区块链技术的实际应用,还展示了NFT在代表数字所有权和身份方面在不断演变的Web空间中的潜力。
为了加深对本教程中使用的技术和概念的理解,考虑查阅以下资源:
通过本教程的学习和探索这些额外的资源,您将为深入了解NFT和区块链技术的世界以及在Web开发和数字资产创建中解锁新的可能性做好充分准备。
原文链接:Create Your Own NFT Gallery with React and Chainbase API
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!