How I solved my code issues while building a blockchain Donation App with a token I created?

Photo by Zan on Unsplash

How I solved my code issues while building a blockchain Donation App with a token I created?

Introduction

I am Amara, a software developer and a budding blockchain developer. I have always belonged to the school of thought that Blockchain is a game changer in every sector.

To get going, I had to get my hands dirty by building projects, so I decided to build a donation blockchain app that will enable people to donate using my tokens to Children in Africa that needed to get back to school.

The blockchain donation App

I don't want to bore you with the entire process but will be going ahead to take you where I faced some issues and solved them. I believe with basic knowledge of blockchain, react and web3js you will just be fine in understanding the entire process. I wouldn't be making the GitHub link public due to the fact that some developers got blown away and we will be launching soon, so we decided to keep it private till after launching, it becomes open source.

The Entire Setup

  1. Solidity: Solidity is a programming language used to write smart contracts for the Ethereum blockchain. You would need to be familiar with Solidity to create the smart contract that will handle donations in your app.

  2. Remix IDE: Remix is a web-based integrated development environment (IDE) for writing, testing, and deploying smart contracts. You can use Remix to write your smart contract code and test it on a local blockchain.

  3. Truffle: Truffle is a development framework that makes it easier to write, test, and deploy smart contracts. Truffle provides tools for compiling Solidity code, deploying contracts to a blockchain, and interacting with deployed contracts.

  4. Ganache: Ganache is a personal blockchain for Ethereum development. You can use Ganache to create a local blockchain for testing your smart contracts.

  5. Web3.js: Web3.js is a JavaScript library for interacting with the Ethereum blockchain. You can use Web3.js to connect to a blockchain, send transactions, and interact with smart contracts from a web application.

  6. React: React is a JavaScript library for building user interfaces. You can use React to create the FrontEnd of your blockchain donation app.

  7. MetaMask: MetaMask is a browser extension that provides a wallet for storing and managing Ethereum accounts. You can use MetaMask to connect to the Rinkeby testnet and send testnet Ether and tokens.

  8. Rinkeby testnet: Rinkeby is a public Ethereum testnet that enables developers to test their applications with fake Ether and tokens. You can use Rinkeby to test your smart contract and frontend code before deploying to the main Ethereum network.

  9. Infura: Infura is a web3 provider that enables you to connect to the Ethereum network without running a local node. You can use Infura to deploy your smart contract and connect to the Rinkeby testnet from your frontend code.

  10. GitHub: GitHub is a web-based platform for hosting and sharing code repositories. You can use GitHub to store and manage the code for your blockchain donation app.

By using these technologies and tools, you can create a blockchain donation app that enables users to donate tokens securely and transparently on the Rinkeby testnet.

Using the useState hook to manage the state of your app

Having created by token within an hour and launching it on the Rinkeby testnet, I had to move ahead into integration and deployment.

After doing some research and brainstorming, I decided to use the useState hook to manage the state of my app. The useState hook is a built-in React hook that allows you to declare state variables in functional components.

I started by creating a new React project and began writing code to build a simple form. The form would allow users to enter their name, email, and the amount they would like to donate.

You declared a state variable called formData and set its initial value to an empty object using the useState hook.


import React, { useState } from 'react';

function App() {
  const [formData, setFormData] = useState({});

  // ...
}

Next, I wrote a function called handleChange to update the formData state whenever the user entered data into the form. I passed this function as a callback to the onChange event of each input field in the form.


import React, { useState } from 'react';

function App() {
  const [formData, setFormData] = useState({});

  function handleChange(event) {
    const { name, value } = event.target;
    setFormData(prevState => ({ ...prevState, [name]: value }));
  }

  return (
    <form>
      <label>
        Name:
        <input type="text" name="name" onChange={handleChange} />
      </label>
      <label>
        Email:
        <input type="email" name="email" onChange={handleChange} />
      </label>
      <label>
        Amount:
        <input type="number" name="amount" onChange={handleChange} />
      </label>
      <button type="submit">Submit</button>
    </form>
  );
}

I tested my form and noticed that the handleChange function was not updating the formData state as expected. I inspected the console and saw that you were getting an error message that reads:

"TypeError: Cannot read property 'target' of undefined."

I realized that the problem was with the handleChange function. It was trying to access the event.target property, but the event parameter was undefined. I went back to the code and added a check to ensure that the event parameter was defined before accessing its properties.

function handleChange(event) {
  if (event) {
    const { name, value } = event.target;
    setFormData(prevState => ({ ...prevState, [name]: value }));
  }
}

With this change, the form started working correctly. The handleChange function was now properly updating the formData state whenever the user entered data into the form.

Next, I decided to add some conditional rendering to the form. I wanted to display a success message when the user submitted the form successfully. I updated your handleSubmit function to update the state variable formSubmitted to true and render the success message.

function handleSubmit(event) {
  event.preventDefault();
  setFormSubmitted(true);
}

return (
  <div>
    <form onSubmit={handleSubmit}>
      // ...
      <button type="submit">Submit</button>
    </form>
    {formSubmitted && (
      <p>Thank you for your donation!</p>
    )}
  </div>
);

I tested my app again and noticed that the success message was not displayed after submitting the form. I inspected the console and saw that I was getting an error message that reads:

"ReferenceError: setFormSubmitted is not defined."

I realized that the problem was with the handleSubmit function. It was trying to call a function called, setFormSubmittedbut I had not declared this function anywhere. I went back to the code and added the setFormSubmitted function using the useState hook, just like you did with the formData state.

function App() {
  const [formData, setFormData] = useState({});
  const [formSubmitted, setFormSubmitted] = useState(false);

  function handleChange(event) {
    if (event) {
      const { name, value } = event.target;
      setFormData(prevState => ({ ...prevState, [name]: value }));
    }
  }

  function handleSubmit(event) {
    event.preventDefault();
    setFormSubmitted(true);
  }

  return (
    <div>
      <form onSubmit={handleSubmit}>
        <label>
          Name:
          <input type="text" name="name" onChange={handleChange} />
        </label>
        <label>
          Email:
          <input type="email" name="email" onChange={handleChange} />
        </label>
        <label>
          Amount:
          <input type="number" name="amount" onChange={handleChange} />
        </label>
        <button type="submit">Submit</button>
      </form>
      {formSubmitted && (
        <p>Thank you for your donation!</p>
      )}
    </div>
  );
}

With this change, my app was working perfectly. The handleChange function was updating the formData state as expected, and the handleSubmit function was updating the formSubmitted state and display the success message when the user submitted the form.

After successfully implementing the useState hook to manage form state in my blockchain donation app, I continued to make progress towards my goal. I connected my app to the blockchain network using web3.js and created a smart contract that would enable users to make donations and track their transactions on the blockchain.

As I tested the app, I noticed a new issue. When users made donations, the app would display a success message, but it wouldn't update the total amount of donations received. I realized that I needed to use the useEffect hook to fetch data from the blockchain and update the donation total whenever a new donation was made.

I started by adding a new state variable called totalDonations using the useState hook.

function App() {
  const [formData, setFormData] = useState({});
  const [formSubmitted, setFormSubmitted] = useState(false);
  const [totalDonations, setTotalDonations] = useState(0);

  // ...
}

Then I added a new useEffect hook that would fetch the total amount of donations from the smart contract whenever a new donation was made.

function App() {
  const [formData, setFormData] = useState({});
  const [formSubmitted, setFormSubmitted] = useState(false);
  const [totalDonations, setTotalDonations] = useState(0);

  useEffect(() => {
    async function getTotalDonations() {
      // Get the total amount of donations from the smart contract
      const contract = new web3.eth.Contract(contractAbi, contractAddress);
      const totalDonations = await contract.methods.getTotalDonations().call();
      setTotalDonations(totalDonations);
    }

    getTotalDonations();
  }, [formSubmitted]);

  // ...
}

After successfully implementing the useState hook to manage form state in your blockchain donation app, you continued to make progress towards your goal. You connected your app to the blockchain network using web3.js and created a smart contract that would enable users to make donations and track their transactions on the blockchain.

As you tested your app, you noticed a new issue. When users made donations, the app would display a success message, but it wouldn't update the total amount of donations received. You realized that you needed to use the useEffect hook to fetch data from the blockchain and update the donation total whenever a new donation was made.

You started by adding a new state variable called totalDonations using the useState hook.

jsxCopy codefunction App() {
  const [formData, setFormData] = useState({});
  const [formSubmitted, setFormSubmitted] = useState(false);
  const [totalDonations, setTotalDonations] = useState(0);

  // ...
}

Then you added a new useEffect hook that would fetch the total amount of donations from the smart contract whenever a new donation was made.

jsxCopy codefunction App() {
  const [formData, setFormData] = useState({});
  const [formSubmitted, setFormSubmitted] = useState(false);
  const [totalDonations, setTotalDonations] = useState(0);

  useEffect(() => {
    async function getTotalDonations() {
      // Get the total amount of donations from the smart contract
      const contract = new web3.eth.Contract(contractAbi, contractAddress);
      const totalDonations = await contract.methods.getTotalDonations().call();
      setTotalDonations(totalDonations);
    }

    getTotalDonations();
  }, [formSubmitted]);

  // ...
}

In this code snippet, I created a new function called getTotalDonations that uses web3.js to interact with the smart contract and get the total amount of donations. I called this function inside a useEffect hook that runs whenever formSubmitted changes. This way, whenever a new donation is made, the useEffect hook will update the totalDonations state and cause the app to display the updated donation total.

As you continued to refine your blockchain donation app, I encountered new challenges and opportunities to improve the code. One of the challenges I faced was dealing with asynchronous data loading when fetching data from the blockchain. I realized that the useEffect hook I was using to fetch data was causing unnecessary re-renders of the components, which slowed down the app and made it less responsive.

To address this issue, I turned to the useCallback hook, which enables one to memoize functions and prevent unnecessary re-renders. I started by creating a new function called fetchTotalDonations using the useCallback hook.

function App() {
  const [formData, setFormData] = useState({});
  const [formSubmitted, setFormSubmitted] = useState(false);
  const [totalDonations, setTotalDonations] = useState(0);

  const fetchTotalDonations = useCallback(async () => {
    // Get the total amount of donations from the smart contract
    const contract = new web3.eth.Contract(contractAbi, contractAddress);
    const totalDonations = await contract.methods.getTotalDonations().call();
    setTotalDonations(totalDonations);
  }, [setTotalDonations]);

  useEffect(() => {
    fetchTotalDonations();
  }, [fetchTotalDonations]);

  // ...
}

In this code snippet, I created a new function called fetchTotalDonations using the useCallback hook. This function uses web3.js to interact with the smart contract and get the total amount of donations. By wrapping this function with the useCallback hook, I ensured that it would only be re-created when the setTotalDonations function changed. This prevented unnecessary re-renders of your components and made the app more responsive.

I also updated the useEffect hook to use the fetchTotalDonations function instead of defining the function inside the hook. This ensured that the useEffect hook would only re-run when the fetchTotalDonations function changes.

With this change, the app was working perfectly. Users could make donations and see the updated donation total in real-time. I continued to refine the app and implement new features, such as a progress bar that displayed the percentage of the donation goal reached, and a social sharing button that enabled users to share their donations on social media. a leaderboard that displayed the top donors and a transaction history that showed all donations made on the blockchain.

Conclusion

Throughout my journey of building the blockchain donation app, the useState, useCallback and useEffect hooks were essential tools that helped me manage the state and update the app in response to user actions and external data sources. By leveraging the power of these hooks and other React features, I was able to create a dynamic and user-friendly app that fulfilled your vision of enabling secure and transparent donations on the blockchain.

What I learned from this project and Challenges 🤗

You never know how much you know until you put in the work and much practice.

Thanks for reading! ❤️ This article is the #DebuggingFeb Eleftheria Batsou challenge. NB: This article was published as the ##DebuggingFeb article for the Hashnode writeathon.

If you liked this article please follow me on Hashnode for my latest articles. I'm tweeting my journey on Twitter daily, this way to my LinkedIn . Hasnode

I share my knowledge on,

  • 🌐 Web Development

  • ✍️ Content Creation

  • 💼 Career Development

  • 🦾Personal Growth

  • BlockChain

  • And more!

References

React useState:React useState

React useEffect: React useEffect

React useCallback: React useCallback

Launching a decentralized App: Blog