DEV Community

Abto Software
Abto Software

Posted on • Updated on

Step-by-step: ReactJS dApp with the OnFlow emulator

This article was created thanks to @vitiv (Yavhen Vitiv)!


In contrast to traditional applications that have their back-end parts running on centralized servers, dApp’s backend code runs autonomously in a decentralized network, like the OnFlow network.

Image description

Smart contracts are programs used for agreement execution running when predetermined conditions are met. They allow involved parties to safely release funds, register vehicles, issue tickets, and even send notifications without intermediaries.

Smart contracts provide for high-level security and transparency, speed, accuracy, and notable cost efficiency. These can be used in various different industries, including healthcare, distribution, retail, and more.
dApps allow to interact with smart contracts deployed on the blockchain.

Image description

How to build a client-side application that interacts with smart contract in the blockchain network

The application we're about to build will have the following functionality:

  • Log-in and log-out buttons
  • User profiles
  • The opportunity to update user profiles
  • Account address directly connected to the blockchain network

Step 1. Installing dependencies

  1. Install the Node Package Manager (NPM)
  2. Install the Flow CLI to run local emulator
  3. Clone the fcl-dev-wallet repository in a separate folder to run dev wallet and the npm install

Step 2. Setting up ReactJS dApp and the npm packages

Create a ReactJS dApp:
npx create-react-app my-first-dapp

Install the OnFlow packages from the my-first-dapp directory:

npm i @onflow/fcl @onflow/types

Step 3. Setting up Tailwind CSS

This step is optional, but if you like, you can read how to set up Tailwind CSS.

Step 4. Running the local emulator with the dev wallet

In the fcl-dev-wallet repository, you will have to run these two commands:
flow emulator
npm run dev

Step 5. Creating the AppJS component with built-in log-in and log-out buttons

In the src/AppJS file, you can create the AppJS component with the log-in and log-out logic:

mport * as fcl from "@onflow/fcl";

import './App.css';
import { useEffect, useState } from "react";

fcl
    .config()
    // Point App at Emulator REST API
    .put("accessNode.api", "http://localhost:8888")
    // Point FCL at dev-wallet (default port)
    .put("discovery.wallet", "http://localhost:8701/fcl/authn")


function App() {

    const [isLogged, setIsLogged] = useState(false);
    const [user, setUser] = useState();

    useEffect(() => {
        fcl.currentUser.subscribe((account) => {
            setUser(account);
            setIsLogged(account.addr)
        });
    }, [])

    const logIn = () => {
        fcl.authenticate();
    }

    const logOut = () => {
        fcl.unauthenticate();
    }

    return (
        <div className="min-h-full flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
            {isLogged ? (
                <div className="p-3">
                    <h3 className="text-2xl">{user.addr}</h3>
                    <button onClick={logOut}
                            className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
                        Logout
                    </button>
                </div>
            ) : (
                <button onClick={logIn}
                        className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
                    Login
                </button>
            )}
        </div>
    );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Step 6. Running the ReactJS dApp

Run the npm-start command and open the page in http://localhost:3000/

Step 7. Creating first smart contract

Create the Profile.cdc file in the fcl-dev-wallet/cadence/contract directory:

Image description

Add the smart contract to the configuration file of the fcl-dev-wallet/flow.json directory:

{
  
  "contracts": {
    "Profile": {
      "source": "./cadence/contracts/Profile.cdc"
      "aliases": {
        "emulator": "0xee82856bf20e2aa6"
     }
    },
    
  },
  
  "deployments": {
    "emulator": {
      "emulator-account": ["FUSD", "Profile"]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Deploy the Profile.cdc file to the local emulator:

flow project deploy --update

Step 8. Creating first cadence script

Extract data from the blockchain network:

const getProfile = async () => {
        const [firstName, lastName, email] = await fcl.send([
            fcl.script`
            import Profile from 0xf8d6e0586b0a20c7

            pub fun main(): [String] {
              return [Profile.firstName, Profile.lastName, Profile.email]
            }
            `
        ]).then(fcl.decode)

        setProfile({ firstName, lastName, email })
    }
view raw
Enter fullscreen mode Exit fullscreen mode

Step 9. Creating first blockchain transaction

Create the blockchain transaction to modify the data in the blockchain network:

const updateProfile = async () => {
        const txId = await fcl.send(
            [
                fcl.transaction`
                import Profile from 0xProfile

                transaction(newFirstName: String, newLastName: String, newEmail: String) {

                  prepare(signer: AuthAccount) {

                  }

                  execute {
                    Profile.updateProfile(newFirstName: newFirstName, newLastName: newLastName, newEmail: newEmail)
                  }
                }
                `,// update profile transaction
                fcl.args([
                    fcl.arg(profile.firstName, t.String),
                    fcl.arg(profile.lastName, t.String),
                    fcl.arg(profile.email, t.String)
                ]), // parameters of the transaction
                fcl.proposer(fcl.authz), // The account that initiate this transaction
                fcl.payer(fcl.authz), // The account that pays the fee for this transaction (free on local emulator)
                fcl.authorizations([fcl.authz]), // A list of the accounts that are authorizing this transaction to mutate to their on-chain account state.
                fcl.limit(9999), // The maximum number of computational units that can be used to execute this transaction.
            ]
        ).then(fcl.decode);

        alert(`The transaction executed successfully`);
    }
Enter fullscreen mode Exit fullscreen mode

Step 10. Updating content

Add the cadence script and the blockchain transaction to the AppJS component and create a from to update the content (user profile):

import * as fcl from "@onflow/fcl";
import * as t from "@onflow/types"

import './App.css';
import { useEffect, useState } from "react";

fcl
    .config()
    // Point App at Emulator REST API
    .put("accessNode.api", "http://localhost:8888")
    // Point FCL at dev-wallet (default port)
    .put("discovery.wallet", "http://localhost:8701/fcl/authn")
    .put("0xProfile", "0xf8d6e0586b0a20c7");

function App() {

    const [isLogged, setIsLogged] = useState(false);
    const [user, setUser] = useState();
    const [profile, setProfile] = useState();

    useEffect(() => {
        fcl.currentUser.subscribe((account) => {
            setUser(account);
            setIsLogged(account.addr)
            getProfile();
        });
    }, [])

    const getProfile = async () => {
        const [firstName, lastName, email] = await fcl.send([
            fcl.script`
            import Profile from 0xProfile

            pub fun main(): [String] {
              return [Profile.firstName, Profile.lastName, Profile.email]
            }
            `
        ]).then(fcl.decode)

        setProfile({ firstName, lastName, email })
    }

    const updateProfile = async (e) => {
        e.preventDefault();

        const txId = await fcl.send(
            [
                fcl.transaction`
                import Profile from 0xProfile

                transaction(newFirstName: String, newLastName: String, newEmail: String) {

                  prepare(signer: AuthAccount) {

                  }

                  execute {
                    Profile.updateProfile(newFirstName: newFirstName, newLastName: newLastName, newEmail: newEmail)
                  }
                }
                `,// update profile transaction
                fcl.args([
                    fcl.arg(profile.firstName, t.String),
                    fcl.arg(profile.lastName, t.String),
                    fcl.arg(profile.email, t.String)
                ]), // parameters of the transaction
                fcl.proposer(fcl.authz), // The account that initiate this transaction
                fcl.payer(fcl.authz), // The account that pays the fee for this transaction (free on local emulator)
                fcl.authorizations([fcl.authz]), // A list of the accounts that are authorizing this transaction to mutate to their on-chain account state.
                fcl.limit(9999), // The maximum number of computational units that can be used to execute this transaction.
            ]
        ).then(fcl.decode);

        console.log(txId);
        alert(`The transaction executed successfully`);
    }

    const logIn = () => {
        fcl.authenticate();
    }

    const logOut = () => {
        fcl.unauthenticate();
    }

    const handleInputChange = (e) => {
        setProfile({ ...profile, [e.target.id]: e.target.value });
    }


    const profileBlock = profile ? (<div className="border-b-2 block md:flex">
        <div className="w-full p-8 bg-white shadow-md">
            <form onSubmit={updateProfile} autoComplete="off">
                <div className="pb-6">
                    <label htmlFor="firstName"
                          className="font-semibold text-gray-700 block pb-1">First Name</label>
                    <div className="flex">
                        <input id="firstName" required className="border-1 bg-gray-200 rounded-r px-4 py-2 w-full"
                              type="text" value={profile.firstName} onChange={handleInputChange}/>
                    </div>
                </div>
                <div className="pb-6">
                    <label htmlFor="lastName"
                          className="font-semibold text-gray-700 block pb-1">Last Name</label>
                    <div className="flex">
                        <input id="lastName" required className="border-1 bg-gray-200 rounded-r px-4 py-2 w-full"
                              type="text" value={profile.lastName} onChange={handleInputChange}/>
                    </div>
                </div>
                <div className="pb-6">
                    <label htmlFor="email"
                          className="font-semibold text-gray-700 block pb-1">Email</label>
                    <div className="flex">
                        <input id="email" autocomplete="nope" required
                              className="border-1 bg-gray-200 rounded-r px-4 py-2 w-full"
                              type="email" value={profile.email} onChange={handleInputChange}/>
                    </div>
                </div>
                <div>

                    <button type="submit"
                            className="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded">
                        Update Profile
                    </button>
                </div>
            </form>
        </div>
    </div>) : '';

    return (
        <div className="min-h-full flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
            {isLogged ? (
                <div className="p-3">
                    <span className="inline-block text-2xl mr-2">Account address: {user.addr}</span>
                    <button onClick={logOut}
                            className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
                        Logout
                    </button>
                    {profileBlock}
                </div>
            ) : (
                <button onClick={logIn}
                        className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
                    Login
                </button>
            )}
        </div>
    );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Now you can test you first decentralized application.
__
There are around 4,000 decentralized applications being utilized in government, healthcare and even gambling. And the positive dynamics undoubtedly attest the feasibility and profitability of adopting blockchain solutions. When speaking about their market dominance, decentralized apps are unlikely to replace traditional apps. However, they are expected to get more momentum in the business environment.

Here are some statistics to consider:

  • Over 199000 Bitcoin transactions are carried out in a single day
  • 10 percent of the global population own cryptocurrencies
  • 16 percent of Americans have invested in cryptocurrency
  • The global Blockchain market is expected to reach $34 billion by 2026 In dApps, personal and business data is stored in separate immutable blocks on the blockchain environment. This means, sensitive data is protected from exploitation, one of the most serious issues, which might be faced by both young startups and mature international corporations.

Top comments (3)

Collapse
 
sarkariprep profile image
Sarkariprep

Wow Rally Nice Information I Like It sarkariprep.in

Collapse
 
monichaudharyx profile image
Moni Chaudhary • Edited

This is h amazing post work it (indjobalert.in)

Collapse
 
emran360 profile image
Md. Emran Hossain

Thanks for this amazing article. You can visit our proggapon website.