Cookbook
Provider
If you want to use NEAR throughout your React app, you would probably want to add a provider to wrap your component tree in to serve the NEAR connection object in it's context.
Here is an example of how to do this.
src/lib/near-provider.js
import { createContext, useState, useEffect } from 'react';import { connect, WalletConnection } from 'near-api-js';export const NearContext = createContext({});export const NearProvider = ({ children, config }) => { const [near, setNear] = useState(null); const [wallet, setWallet] = useState(null); useEffect(() => { async function connectNear() { const near = await connect(config); setNear(near); setWallet(new WalletConnection(near)); } connectNear().catch(console.error); }, []); const isConnected = Boolean(near && wallet); return ( <NearContext.Provider value={{ near, wallet }}> {isConnected && children} </NearContext.Provider> );};
Config
src/config.js
import { keyStores } from 'near-api-js';/** * Function that returns a NEAR connection configuration object based on the given networkId. * * @param {string} networkId='testnet' * @return {object} */export const getConfig = (networkId = 'testnet') => { return { keyStore: new keyStores.BrowserLocalStorageKeyStore(), networkId, nodeUrl: `https://rpc.${networkId}.near.org`, walletUrl: `https://wallet.${networkId}.near.org`, helperUrl: `https://helper.${networkId}.near.org`, explorerUrl: `https://explorer.${networkId}.near.org`, };};
And then use it to wrap your entire app.
src/index.js
import React from 'react';import ReactDOM from 'react-dom/client';import App from './App';import { NearProvider } from './lib/near-provider';import { getConfig } from './config';import { Buffer } from 'buffer';global.Buffer = Buffer;const root = ReactDOM.createRoot(document.getElementById('root'));root.render( <React.StrictMode> <NearProvider config={getConfig('testnet')}> <App /> </NearProvider> </react.StrictMode>,);
Hooks
You might want to create custom hooks to use common functionality across your app.
Here are some example hooks you can use:
note
All of these hooks need to be inside of the NearProvider
component subtree in order to access the NEAR connection.
useNear
src/lib/useNear.js
import { useContext } from 'react';import { NearContext } from './lib/near-provider';/** * Get the NEAR connection object from the context. */export const useNear = () => { const { near } = useContext(NearContext); return near;};
Example use case:
src/NearComponent.jsx
import React from 'react';import { useNear } from './lib/useNear';export const NearComponent = () => { const near = useNear(); // This will display the current network we are connected to e.g. 'testnet' or 'mainnet' return <div>{near.connection.networkId}</div>;};
useWallet
src/lib/useWallet.js
import { useContext } from 'react';import { NearContext } from './lib/near-provider';/** * Get the NEAR wallet connection object from the context. */export const useWallet = () => { const { wallet } = useContext(NearContext); return wallet;};
Example use case:
src/WalletComponent.jsx
import React from 'react';import { useWallet } from './lib/useWallet';export const WalletComponent = () => { const wallet = useWallet(); // This will display the address of the currently signed in account return wallet.isSignedIn() ? <div>{wallet.getAccountId()}</div> : null;};
useContract
src/lib/useContract.js
import { useContext } from 'react';import { NearContext } from './lib/near-provider';import { Contract } from 'near-api-js';/** * Create a new contract object from the NEAR wallet object given the id and methods of * the smart contract. * * @param {Object} contractConfig The smart contract configuration. * @param {string} contractConfig.contractId The id of the smart contract. * @param {Object} contractConfig.contractMethods The methods of the smart contract. * @param {string[]} contractConfig.contractMethods.viewMethods The view methods of the smart contract. * @param {string[]} contractConfig.contractMethods.changeMethods The change methods of the smart contract. */export const useContract = ({ contractId, contractMethods: { viewMethods, changeMethods },}) => { const wallet = useWallet(); return new Contract(wallet.account(), contractId, { viewMethods, changeMethods, });};
Example use case:
src/ContractComponent.jsx
import React, { useState, useEffect } from 'react';import { useContract } from './lib/useContract';import { useWallet } from './lib/useWallet';export const ContractComponent = () => { // Here we define the contract configuration and get a contract object. const contract = useContract({ contractId: 'wrap.testnet', contractMethods: { viewMethods: ['ft_balance_of'], changeMethods: [], }, }); // We use the `useWallet` hook to get the wallet connection object. const wallet = useWallet(); // Since executing smart contract methods takes time we will use a `useState` hook to // store the result of the method execution. const [balance, setBalance] = useState(0); useEffect(() => { // Calling smart contract methods is an async task so we create a async function to // execute the method. async function getBalance() { // We check to see if an account is signed in, otherwise we cannot get a balance of an unknown account. if (wallet.isSignedIn()) { // We store the return value of the smart contract call in the `balance` variable. setBalance(await contract.ft_balance_of(wallet.getAccountId())); } } getBalance().catch(console.error); }, [wallet, contract]); // This will display the available wNEAR balance of the currently signed in account return wallet.isSignedIn() ? <div>{balance}</div> : null;};
Examples
Here are some examples of common use cases:
Sign in button
src/components/SignInButton.jsx
import React from 'react';import { useWallet } from './lib/useWallet';const SignInButton = ({ config }) => { const wallet = useWallet(); const signIn = () => wallet.requestSignIn(config); return wallet.isSignedIn() ? ( <p>{wallet.getAccountId()}</p> ) : ( <button onClick={() => signIn()}>Sign in with NEAR</button> );};export default SignInButton;
Sending tokens
src/components/SendTokens.jsx
import React, { useState, useEffect } from 'react';import { utils } from 'near-api-js';import { useWallet } from './lib/useWallet';const { format: { parseNearAmount },} = utils;const SendTokens = () => { const wallet = useWallet(); const [amount, setAmount] = useState(0); const [recipient, setRecipient] = useState(''); if (!wallet.isSignedIn()) { return null; } const sendTokens = async () => { // The account object allows us to send tokens via the `sendMoney` method. // P.S. We need to call the `parseNearAmount` function to convert the amount of NEAR // the user wants to send to yoctoNEAR (1e-24 NEAR) because the network stores values // in yoctoNEAR. await wallet.account().sendMoney(recipient, parseNearAmount(amount)); }; return ( <div> <label> Amount:{' '} <input type="number" value={amount} onChange={({ target: { value } }) => setAmount(value)} /> </label> <label> Recipient:{' '} <input type="text" value={recipient} onChange={({ target: { value } }) => setRecipient(value)} /> </label> <button onClick={() => sendTokens()}>Send</button> </div> );};