Home > other >  Trying to get a fetch request to finish before parsing (React)
Trying to get a fetch request to finish before parsing (React)

Time:04-21

I am trying to avoid a list of objects having a value of '{}'

import { IRootState } from 'app/shared/reducers';
import { getEntity } from './determination.reducer';
import { IDetermination } from 'app/shared/model/tpa/determination.model';
import React, { lazy, useEffect } from 'react';
import { connect } from 'react-redux';
import { Link, RouteComponentProps } from 'react-router-dom';

export interface IDeterminationDetailProps extends StateProps, DispatchProps, RouteComponentProps<{ id: string }> {}


export const DeterminationDetail = (props: IDeterminationDetailProps) => {
  useEffect(() => {
    props.getEntity(props.match.params.id);
  }, []);
  
  function waitForElementProps(){
    if(props != null && props){
      console.log('finished loading props.. '   props.determinationEntity)
    } else { 
      setTimeout(waitForElementProps, 5000);
    }
  }

  waitForElementProps();

  var {determinationEntity} = props;
  
  const obj = JSON.stringify(determinationEntity);
  
  if(obj === '{}') {
    console.log('caught')
    waitForElementProps();
  }

  if(!obj || obj==null || obj ==='{}'){
    waitForElementProps();
  }

  waitForElementProps();

  console.log('new '   obj);

In the console output I get

finished loading props.. [object Object] 

caught 

finished loading props.. [object Object] 

new {} 

determination/FETCH_DETERMINATION_FULFILLED 

finished loading props.. [object Object] 2

new {"determination":{"id":1051,"a ... }

How does my code possibly assign the value of '{}' to 'obj' even though I am calling a recursive function before it can even get there?

This problem relates to me wanting to wait for the data to arrive before I parse it, because I keep getting the issue of 'obj is undefined' whenever I try to parse it.

I know there is async await in javascript where I can wait for the 'FETCH_DETERMINATION_FULFILLED' to finish but I don't know how to implement it. Here is my api call (from a separate class)

export const getEntities: ICrudGetAllAction<IDetermination> = (page, size, sort) => {
  const requestUrl = `${apiUrl}${sort ? `?page=${page}&size=${size}&sort=${sort}` : ''}`;
  return {
    type: ACTION_TYPES.FETCH_DETERMINATION_LIST,
    payload: axios.get<IDetermination>(`${requestUrl}${sort ? '&' : '?'}cacheBuster=${new Date().getTime()}`),
  };
};

CodePudding user response:

React (and Javascript) is essentially asynchronous. It is not possible to block waiting for an asynchronous operation to complete. Even async/await doesn't actually block, it's just syntactic sugar for chains of .then.

The react way to handle this would either be to enforce that the parent component always passes the props you want, or to render null when the props aren't populated so that nothing is rendered to the DOM until you have loaded your data. Like so:

if (!props.determinationEntity) {
    return null;
}

A few other notes:

It is not possible for the props parameter to be null. If no props are passed React passes an empty object.

if (props != null && props) is redundant, null is falsey in Javascript, so that is equivalent to if (props)

CodePudding user response:

In short,

  • async/await is the ideal answer, but
  • add null checking, and
  • using setTimeout is not thread-blocking

async/await

Ideally, you would await the response from the API in the useEffect hook you implemented. This means that the code will WAIT for the API to respond before moving onto the next line of the useEffect hook. Then you can save the value from the response in a state object to use later. That might look like something as easy as this:

useEffect(() => {
  const getData = async () => {
    const res = await props.getEntity(props.match.params.id);
    const resData = await res.json();

    // now save to state
    setData(resData);

    // or parse as you need (as example)
    setName(resData?.details?.name);
  }

  getData();
}, []);

add null checking

From what code you supplied, it is difficult to tell how the values are saved from the API currently to the props.determinationEntity variable. No matter how this update happens, however, you should apply checking when you are using the value in your code later on so that you do not get type errors. Use optional chaining, nullish coalescing, and simple if(!props.determinationEntity) return; statements.

setTimeout is not thread-blocking

Regarding what you said:

How does my code possibly assign the value of '{}' to 'obj' even though I am calling a recursive function before it can even get there?

Invoking the setTimeout inside the waitForElementProps function will not block all code continuation. The timeout will be saved and the code execution will continue through the stack, reaching the declaration for obj and setting it to the string of an empty object.

Also beware of var for a similar reason. It will elevate the declaration to be function-scoped instead of block-scoped like let and const, so you may be able to access variables in places they "shouldn't" be (depending on what you are expecting).

CodePudding user response:

I ended up implementing a cheap workaround for this

here's the code I added

import testData from 'app/shared/testdata/determination.json';

var obj = testData

Essentially all I did was assign it a default value that was the exact structure of the data it expects ('testData' is JSON in the same format as 'obj'). And to prevent any possibility I even added one more 'if' statement before rendering.

  if(obj=== undefined || obj = '{}'){
    obj=testData;
    waitForElementProps;
  }

CodePudding user response:

make use of

useLayouteffect

useLayouteffect(() => {
    waitForElementProps();   }, []);
  • Related