skip to Main Content

I’m trying to make a small app that reads an endpoint to get a list of items. The app will then loop through and pull the details for the items. I did get a version to work but it breaks the rules of hooks. What is the standard / correct way?

This code should trigger the useEffect on load to set the Globals. Then trigger the useEffect to setLocalData and then trigger the useEffect to add the localdata to the localDataList

When I run this code I get the list of the 2 global lines. That works great. The details part is where it gets wonky. First, I can’t figure out how to get rid of the leading null in the local. Secondly, if I refresh over and over, I get only 1 item sometimes.

import React from 'react';
import { useState, useEffect } from 'react';
import { Buffer } from 'buffer';
import algosdk from 'algosdk';

export declare module DataStore {

  export interface Events {
      "global-state": KeyValue[];
  }  
  
  export interface EventDetails {
    "key-value": KeyValue[];
  }

  export interface Value {
      bytes: string;
      type: number;
      uint: number;
  }
  
  export interface KeyValue {
      key: string;
      value: Value;
  }
}

function App() {
  const [globals, setGlobals] = useState<DataStore.Events[]>([]);
  const [localDataList, setLocalDataList ] = useState<any>([])
  const [localData, setLocal] = useState<DataStore.EventDetails[]>();

  useEffect(() => {   
    fetch("https://node.algoexplorerapi.io/v2/applications/1022971265")
    .then((response) => response.json())
    .then((data) => setGlobals(data.params["global-state"].filter((item: any) => item.key !== "VGl0bGU=" )));     
  }, []);

  useEffect(() => {
    globals.forEach(function (item: any)  {
      //console.log(globals);
      const pretty_address = algosdk.encodeAddress(new Buffer(item.value.bytes, 'base64')); 
      //console.log(pretty_address)
      fetch("https://node.algoexplorerapi.io/v2/accounts/" + pretty_address + "/applications/1022971265")
      .then((response) => response.json())
      .then((data) =>  setLocal(data["app-local-state"]["key-value"] )); // 
    })   
  }, [globals]);

  useEffect(() => {
    console.log(localData)
    setLocalDataList([...localDataList,  localData ])
   }, [localData]);

  return (
    <div>
    Globals: 
    <ul>
      {globals.map((item: any) => (
        <li key={item.key}>{
              JSON.stringify(item)
          }</li>
      ))} 
    </ul>
    <br/>
     Local list:
    <ul>
      {localDataList?.map((item) => ( <li> {JSON.stringify(item)} </li> )) }
    </ul>
    </div>
  );
}

export default App;

Thanks for the help.

2

Answers


  1. to remove null, replace

      {localDataList?.map((item) => ( <li> {JSON.stringify(item)} </li> )) }
    

    with

     {localDataList && localDataList?.map((item) => ( <li> {JSON.stringify(item)} </li> )) }
    

    then there seems being a problem with global.foreach, you will repeteadly overwrite the localldata content since your are not adding items, but replacing them with this line :

      .then((data) =>  setLocal(data["app-local-state"]["key-value"] )); // 
    

    prefer :

    .then((data) => setLocal(newdata => [...newdata, data["app-local-state"]["key-value"] ]));
    

    finally, useState<DataStore.EventDetails[]>(); is not initialized

    Login or Signup to reply.
  2. In your situation I would create a free function to do all the work, and return it once completed.

    async function fetchLocalDataList() {
        const res = await fetch("https://node.algoexplorerapi.io/v2/applications/1022971265")
        const data = await res.json()
        const globals = data.params["global-state"].filter((item: any) => item.key !== "VGl0bGU=")
    
        const localDataList = []
    
        for (const item of globals) {
          const pretty_address = algosdk.encodeAddress(new Buffer(item.value.bytes, 'base64')); 
          const res = await fetch("https://node.algoexplorerapi.io/v2/accounts/" + pretty_address + "/applications/1022971265")
          const data = await res.json()
          localDataList.push(data)
        }
    
        return {globals, localDataList}
    }
    

    Then you component can be something like

    function App() {
      const [globals, setGlobals] = useState<DataStore.Events[]>([]);
      const [localDataList, setLocalDataList ] = useState<any>([])
    
      useEffect(() => {   
        fetchLocalDataList().then(({globals, localDataList}) => {
            setGlobals(globals)
            setLocalDataList(localDataList)
        }) 
      }, []);
    
      return (
        <div>
        Globals: 
        <ul>
          {globals.map((item: any) => (
            <li key={item.key}>{
                  JSON.stringify(item)
              }</li>
          ))} 
        </ul>
        <br/>
         Local list:
        <ul>
          {localDataList?.map((item) => ( <li> {JSON.stringify(item)} </li> )) }
        </ul>
        </div>
      );
    }
    

    If you want to fetch from inside components like this I would however sugges you use something like react-query or SWR. It will deal with batching, concurrent calls, caching etc for you and make life easier.

    Also, just to cover all the bases. Error checking here is omitted, but should be included in any code you actually planning to use.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search