0

I'm trying to bring rows of data back from an SQLite database and then iterate through them so I can manipulate the data of each row to present them differently (ie, convert a date into a customised format).

Bringing the data back is fine (from the ReadEntries function), which I do in a useEffect so that it only runs once on the screen load, but the duplicating of the array and then updating the rows doesn't seem to work. I think it might have something to do with the fact setting the value of a useState array isn't quick enough to update for my duplicate array to then take a full snapshot of it.

It sometimes works when I save multiple times in VS Code, presumably because the state is already stored for subsequent screen refreshes.

useEffect(() => {
    var results: SQLite.ResultSetRowList;   
    const response = async (): Promise<any> => {
      await ReadEntries(projectID).then((value) => {
        results = value as SQLite.ResultSetRowList;
        setEntries(results.raw); //this works
        let newEntries = [...entries];      
        for (let i = 0; i < newEntries.length; i++) {
          let newStartDate = new Date(newEntries[i].startDate);
          newEntries[i].dateOfEntry = newEntries[i].dateOfEntry;
          newEntries[i].startDate =
            newStartDate.getDate().toString() +
            "/" +
            newStartDate.getMonth().toString() +
            "/" +
            newStartDate.getFullYear().toString();
        }
       setEntries(newEntries);
      });
    };
    response().catch((error) => {
      "ERROR: " + error;
    });
  }, []);

Thanks

SMcDonald
  • 105
  • 2
  • 10

2 Answers2

0

State updates with hooks are not only async but are also bound by closure. You should not reply on the updated state to trigger another change in the same function call

Check this post for more details on this: useState set method not reflecting change immediately

You can make use of the the fetched data to perform the operation

useEffect(() => {
    var results: SQLite.ResultSetRowList;   
    const response = async (): Promise<any> => {
      await ReadEntries(projectID).then((value) => {
        results = value as SQLite.ResultSetRowList;
        setEntries(results.raw); // This shouldn't be required.
        let newEntries = [...results.raw]; // use results instead of entries      
        for (let i = 0; i < newEntries.length; i++) {
          let newStartDate = new Date(newEntries[i].startDate);
          newEntries[i].dateOfEntry = newEntries[i].dateOfEntry;
          newEntries[i].startDate =
            newStartDate.getDate().toString() +
            "/" +
            newStartDate.getMonth().toString() +
            "/" +
            newStartDate.getFullYear().toString();
        }
       setEntries(newEntries);
      });
    };
    response().catch((error) => {
      "ERROR: " + error;
    });
  }, []);
Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400
  • Thanks for the fast reply! I've tried doing that, but I get the message from 'results.raw' that Type '() => any[]' must have a '[Symbol.iterator]()' method that returns an iterator, and I don't know how to fix it! I was relying on results.raw being handled by first going into the useState function and then copying it from there. I like your idea better, but do you know how I can handle the results.raw information? It returns the following function: function raw() { return rows.slice(); } Thanks – SMcDonald Jun 15 '20 at 09:13
  • You can try `Array.from(result.raw)` instead of `[...results.raw]` – Shubham Khatri Jun 15 '20 at 09:18
  • That comes back with the follow: No overload matches this call. Overload 1 of 4, '(iterable: Iterable | ArrayLike): unknown[]', gave the following error. Argument of type '() => any[]' is not assignable to parameter of type 'Iterable | ArrayLike'. Type '() => any[]' is not assignable to type 'ArrayLike'. Index signature is missing in type '() => any[]'. Overload 2 of 4, '(arrayLike: ArrayLike): unknown[]', gave the following error. Argument of type '() => any[]' is not assignable to parameter of type 'ArrayLike' – SMcDonald Jun 15 '20 at 09:35
0

I achieved this by calling the .raw() function of the returned results, and looping through that array with forEach. I then created a new object (newarrayItem) and assigned the fields to the .raw() array values, then pushed that object into a blank array (newarray) that I assigned the type TentryItemString. Then passed this to the setEntries useState array.

useEffect(() => {
    var results: SQLite.ResultSetRowList;      
    let newarray: TentryItemString[] = [];
    const response = async (): Promise<any> => {
      await ReadEntries(projectID).then((value) => {
        results = value as SQLite.ResultSetRowList;       
        results.raw().forEach((element, index) => {
          let newdate = "";
          let newStartDate = new Date(element.startDate);
          newdate =
            newStartDate.getDate().toString() +
            "/" +
            (newStartDate.getMonth() + 1).toString() +
            "/" +
            newStartDate.getFullYear().toString();

          var newarrayItem = {             
            dateOfEntry: "",
            startDate: "",          
          };         
          newarrayItem.startDate = newdate;
          newarrayItem.dateOfEntry = element.dateOfEntry;
          newarray.push(newarrayItem);
        });
      });
      newarray.reverse();
      setEntries(newarray);
    };
    response().catch((error) => {
      "ERROR: " + error;
    });
  }, []); 

A couple of issues I found in doing this. One, if I initiated the newarrayItem by equalling it to an already created object (ie. var newarray = newarrayItem), the array.push() method seemed to overwrite all rows with the same information. That's why I've instantiated it there and then. The other issue was struggling with assigning the type to the useState as an array, but I finally achieved this with the following syntax:

const [entries, setEntries] = useState();

And all TentryItemString is, is:

type TentryItemString = {
  dateOfEntry: string;
  startDate: string;     
};

Hope someone finds that helpful.

SMcDonald
  • 105
  • 2
  • 10