Integrating NAJ with React

LimeChain
6 min readFeb 6, 2024

--

Introduction

Building decentralized applications (DApps) on the NEAR Protocol requires an easy way to interact with smart contracts. Near-api-js (NAJ) is the most popular JavaScript tool that gives developers a reliable way to communicate with the NEAR Protocol. Combined with React, a widely used JavaScript framework for creating browser user interfaces, NAJ becomes a very helpful tool for making DApps that work smoothly and are fun to use.

In this article, we’ll explore how to use NAJ with React (v18), explaining the critical concepts and steps that every developer should understand. As we dive into the basics of using NAJ with React, we’ll also walk through real-life examples where we interact with the USDT (Tether) smart contract on the NEAR Protocol.

Setting up a React project

Assuming you have Node.js (v20.11.0) and NPM installed, let’s set up a new React project in order to integrate NAJ. Open your terminal and run the following command:

npx create-react-app nearjs-react-app --template typescript

This command creates a new React project named “nearjs-react-app”. After the installation completes, navigate to the project directory:

cd nearjs-react-app

Getting started with NAJ

Before we delve into combining NAJ with React, it’s crucial to grasp the fundamental concepts and capabilities of NAJ. NAJ is a robust JavaScript library designed for applications to communicate with the NEAR Protocol. It comes equipped with various features, such as:

  • Handling accounts
  • Signing transactions
  • Deploying smart contracts
  • Interacting with smart contracts

To start off, install NAJ using your preferred package manager (yarn, pnpm or npm):

yarn add near-api-js
pnpm add near-api-js
npm install near-api-js

Once the installation is complete, you can import NAJ into your project by adding the following line to index.tsx:

import * as nearAPI from 'near-api-js';

Great job! Now the stage is set for integrating NAJ functionalities into your React project.

Initializing NAJ in React

Now that we have our React project set up, let’s initialize NAJ. In the project directory, open the src/index.tsx file and replace the existing code with the following:

import React from "react";
import "./index.css";
import App from "./App";
import { WalletConnection, Near, keyStores } from 'near-api-js'
import ReactDOM from "react-dom";
import { setupWalletSelector } from "@near-wallet-selector/core";
import { setupHereWallet } from "@near-wallet-selector/here-wallet";
import { setupMyNearWallet } from "@near-wallet-selector/my-near-wallet";
import { setupNearWallet } from "@near-wallet-selector/near-wallet";
import { setupNeth } from "@near-wallet-selector/neth";
import { setupNightly } from "@near-wallet-selector/nightly";
import { setupSender } from "@near-wallet-selector/sender";

async function init() {
const near = new Near({
deps: {
keyStore: new keyStores.BrowserLocalStorageKeyStore(),
},
networkId: "mainnet",
nodeUrl: "https://rpc.mainnet.near.org",
helperUrl: "https://helper.mainnet.near.org",
});

const walletConnection = new WalletConnection(
near,
"Nearjs react app"
);

let currentUser;
if (walletConnection.getAccountId()) {
currentUser = walletConnection.getAccountId()
}

const selector = await setupWalletSelector({ network: "mainnet", modules: [
setupNearWallet(),
setupMyNearWallet(),
setupSender(),
setupHereWallet(),
setupNightly(),
setupNeth(),
]
})

return { currentUser, walletConnection, selector };
}

const initializeNear = async () => {
try {
const { currentUser, walletConnection, selector } = await init();
renderApp(currentUser, walletConnection, selector);
} catch (error) {
console.error('Error initializing NEAR:', error);
}
};

initializeNear();

const renderApp = (currentUser: any, walletConnection: any, selector: any) => {
ReactDOM.render(
<React.StrictMode>
<App currentUser={currentUser} wallet={walletConnection} selector={selector} />
</React.StrictMode>,
document.getElementById("root")
);
};

The init() function plays a crucial role in establishing the connection to the NEAR blockchain. It utilizes Near class to configure essential parameters like the network ID and endpoint URLs, setting the stage for interaction. The function also creates a wallet connection through WalletConnection. If a user is logged in, it captures and stores their account ID and balance for later use. The setupWalletSelector function is responsible for configuring a wallet selector, a component that allows users to choose or manage their NEAR wallets.

Once the initialization succeeds, the App component is rendered with important props such as currentUser, selector, and walletConnection, ensuring a seamless integration of NEAR functionalities into the React application.

Connection to NEAR

To interact with a NEAR Protocol network, we need to connect to a specific network. We can achieve this by updating our App component with network connection functionality. Modify the App component code as follows:

import { setupModal } from "@near-wallet-selector/modal-ui";

// ...

function App({ currentUser, wallet, selector }) {
// ...
const [user, setUser] = useState(currentUser);

const handleUser = async (e: any) => {
if (!user) {
const modal = setupModal(selector, {
contractId: "usdt.tether-token.near"
});
modal.show()
} else {
await wallet.signOut();
window.location.reload()
}
};

// ...
};

The handleUser function manages user interactions in your NEAR-powered React app. If someone is signed in and clicks "Sign Out," it smoothly logs the user out. If no one is signed in and "Login" is clicked, then the user goes through the sign-in process, allowing them to assert their identity and perform specific actions tied to the usdt.tether-token.nearcontract.

Interacting with Smart Contracts

One of the core functionalities of NAJ is its ability to interact with smart contracts on the NEAR network. To demonstrate this, let’s write a simple contract interaction function. Update the App component code as follows:

// ...

const [metadata, setMetadata] = useState<{ name: string, symbol: string, decimals: number } | null>(null);
const [loading, setLoading] = useState(false);

// ...

const readMetadata = async () => {
setLoading(true);

try {
const tokenMetadata = await wallet.account().viewFunction({
contractId: "usdt.tether-token.near",
methodName: "ft_metadata",
});
setMetadata(tokenMetadata);
} catch (error) {
console.error("Error fetching metadata:", error);
} finally {
setLoading(false);
}
};

// ...

The readMetadata method is a function that sends a read request to an RPC node. This means that it retrieves information from the blockchain without making changes to its state. The function is designed to be patient and wait for a response. First, it signals that it's starting to execute by setting a loading flag. Then, it attempts to get information (metadata) from a specific smart contract called usdt.tether-token.near by invoking the method ft_metadata. If everything goes smoothly, it takes the received information and sets it using the setMetadata function, which displays the received information.

An alternative for viewFunction(...) is the Contract API. It abstracts some the complexities away. It can be used like this:

const contract = new Contract(wallet.account(), "usdt.tether-token.near", {
viewMethods: ["ft_metadata"],
changeMethods: ["ft_transfer"],
useLocalViewExecution: false,
});
const tokenMetadata = await contract.ft_metadata();

Feel free to use whatever approach you find more appealing. Now lets add the following function:

const stateChangeFunctionCall = async () => {
const functionCallRes = await wallet.account().functionCall({
contractId: "usdt.tether-token.near",
methodName: "ft_transfer",
args: { amount: 100, receiver_id: "example.receiver.near" },
});

// Or you can use the Contract object from the alternative approach like this:
const functionCallRes = await contract.ft_transfer({ amount: 100, receiver_id: "example.receiver.near"})
};

The functionCall method initiates a state-altering transaction to the NEAR Protocol. Such transactions incur a gas cost based on the operations that happened during the execution. The parameters - contract ID and method name, define the smart contract and the function to be invoked. The optional args parameter allows for the inclusion of specific data or parameters for the smart contract function. Upon execution, this transaction triggers the designated method and modifies the blockchain state as per the contract’s logic.

We don’t want users accidentally executing ft_transfer() transactions. That’s why the function call hasn’t been wired into the UI. If you wish to do that, it’s as simple as adding a new button just like the “Read metadata” one but for “Transfer” instead.

Running the App

Everything is set up. Let’s start the application.

Start the project

yarn start

Connect to Near

Open the application in your browser, and you should see a login button. Click on it to initiate the NEAR wallet login process. Follow the prompts to connect your NEAR wallet to the application.

Read contract state

After successfully logging in, you will see a “Read metadata” button. Click on it to fetch the contract metadata. After some time, the following contract data will be displayed:

  • Token name
  • Token symbol
  • Token decimals

Code in action

Conclusion

Throughout this guide, we’ve delved into the fusion of NAJ and React for crafting decentralized applications on the NEAR Protocol. We’ve walked through crucial steps like setting up NAJ, connecting to the NEAR network, and interacting with smart contracts. By grasping these basics, you’re well-prepared to kick off your DApp development adventure using NAJ and React. Don’t forget to check out the official documentation and other resources to enhance your understanding and unlock the full capabilities of NAJ.

If you’d like to explore the tutorial’s code, you can find its the repository on GitHub

Future Plans

Looking forward, we plan to expand the coverage of this tutorial to include other significant JavaScript frameworks like Angular, Svelte, and Vue. The goal is to create comprehensive guides, similar to the existing one. Whether you’re working with Angular, exploring Svelte, or utilizing Vue, we aim to provide detailed step-by-step instructions for integrating NAJ seamlessly. Stay tuned for more tutorials, enabling developers in various ecosystems to confidently build decentralized applications on the NEAR Protocol.

--

--

LimeChain

Innovative Blockchain & DLT Services and Solutions for Enterprises and Entrepreneurs. More at https://limechain.tech/