Search code examples
javascriptreactjspolkadot

Create an array within a 'for loop' in React


I have picked up a React project that uses Polkadot.js from another developer and I'm struggling to understand how to achieve the following:

I want to display a grid of images. The initial part of the image URL is hardcoded in a const.

When the user clicks on a button, the user's account is checked asynchronously to see what NFT's they own. However there isn't an array/object what I need, so I have to create an array using the hardcoded URL, along with the provided number and add the image file extension at the end of the string. eg. imageURL + imageNumber + '.png'

The async function that is called on click is as follows:

const getNFT = async (address: string) =>  {
    const wsProvider = new WsProvider(wsUrl);
    const api = await ApiPromise.create({ provider: wsProvider });

    // NOTE the apps UI specified these in mega units -> https://polkadot.js.org/docs/api-contract/start/contract.read
    // @ts-ignore
    const gasLimit = -1;
    // Read from the contract via an RPC call
    const value = 0; // only useful on isPayable messages -> https://polkadot.js.org/docs/api-contract/start/contract.read

    // @ts-ignore
    const contract = new ContractPromise(api, abi, contractAddress);

    const {gasConsumed, result, output} = await contract.query['psp34::balanceOf'](address, {value: 0, gasLimit: -1}, address);
    console.log(JSON.stringify(result.toHuman()));
    if (result.isOk) {
      // should output 123 as per our initial set (output here is an i32)
      // @ts-ignore
      setUserNFTCount(output.toHuman());

      // @ts-ignore
      console.log('Number of NFT you own:', output.toHuman());

      // @ts-ignore
      for (let i = 0; i <= output.toNumber() - 1; i++) {
        console.log ("Getting NFT at index " + i);
        const NFTIndexResult = await contract.query['psp34Enumerable::ownersTokenByIndex'](address, { value: 0, gasLimit: -1 }, address, i);
        
        if (NFTIndexResult.result.isOk && NFTIndexResult.output != null) {
          console.log(NFTIndexResult);
          console.log(NFTIndexResult.output.toHuman())
           // @ts-ignore
          var pNumber = NFTIndexResult.output.toHuman().Ok.U64;
          console.log('NFT number you own:' + pNumber);

          var metadata = metadataUrl + pNumber + '.json';
          var image = imageUrl + pNumber + '.png';

          console.log('TODO, show image: ' + image);
          let myImageArray = [];
          myImageArray.push(image)
          console.log('array = ' + myImageArray);
        }
      }

    } else {
      console.error('Error', result.asErr);
    }
  }

When I check my console, I can see the individual images I want to add to an array within the image variable. But I'm not sure how to add each image into a single array which I can then use to render a grid of the images.

Edit - For further context: When I do a console log, I only see each individual image path per line rather than an array, even when moving the console log outside of the loop eg. (www.somewebsite.com/image1.png or www.somewebsite.com/image2.png)


Solution

  • Declare the array outside the loop so it isn't declaring a new array each iteration.

    Example:

    const myImageArray = []; // <-- declare prior to loop
    
    // @ts-ignore
    for (let i = 0; i <= output.toNumber() - 1; i++) {
      console.log ("Getting NFT at index " + i);
      const NFTIndexResult = await contract.
        query['psp34Enumerable::ownersTokenByIndex'](
          address,
          { value: 0, gasLimit: -1 },
          address,
          i,
        );
        
      if (NFTIndexResult.result.isOk && NFTIndexResult.output != null) {
        console.log(NFTIndexResult);
        console.log(NFTIndexResult.output.toHuman());
        // @ts-ignore
        var pNumber = NFTIndexResult.output.toHuman().Ok.U64;
        console.log('NFT number you own:' + pNumber);
    
        var metadata = metadataUrl + pNumber + '.json';
        var image = imageUrl + pNumber + '.png';
    
        console.log('TODO, show image: ' + image);
          
        myImageArray.push(image); // <-- push all into same array
        console.log('array = ' + myImageArray);
      }
    }
    

    Presumably you'll be doing something with myImageArray after the loop.

    Update using array of Promise objects

    You might find it easier to collect the fetched images using an array of Promise objects.

    Example:

    const promises = Array.from({ length: output.toNumber()})
      .map((_, i) => {
        return contract.
          query['psp34Enumerable::ownersTokenByIndex'](
            address,
            { value: 0, gasLimit: -1 },
            address,
            i,
          )
          .then(NFTIndexResult => {
            if (NFTIndexResult.result.isOk && NFTIndexResult.output != null) {
              console.log(NFTIndexResult);
              console.log(NFTIndexResult.output.toHuman());
              // @ts-ignore
              const pNumber = NFTIndexResult.output.toHuman().Ok.U64;
              console.log('NFT number you own:' + pNumber);
    
              const metadata = metadataUrl + pNumber + '.json';
              const image = imageUrl + pNumber + '.png';
    
              console.log('TODO, show image: ' + image);
          
              return image;
            }
          });
      });
    
    const myImageArray = await Promise.all(promises);
    
    console.log('array = ' + myImageArray);