ellucian-developer / experience-ethos-examples

Example Experience Extensions which use Ethos to retrieve data from the ERP.
Apache License 2.0
28 stars 9 forks source link

getItem is not async #9

Closed kishba closed 2 years ago

kishba commented 2 years ago

https://github.com/ellucian-developer/experience-ethos-examples/blob/89fd06505d32d2efbf30319cb4a6240c50aca40b/account-details-extension/src/context/account-details.js#L50

I also adopted getItem, storeItem, and removeItem from the SDK and connected it to react-query! Our approaches are similar, but I was able to remove some of the complexity surrounding this particular line by removing the await in front of getItem. The SDK docs don't indicate it's an asynchronous method, and when I added await, a new promise was being created and causing the component to render without data.

Another simplification I found was to use initialData instead of placeholderData and define it as a function so react-query will only call it on the first render. I would be very interested to know if you picked placeholderData over initialData for a particular reason!

One more thing I wanted to mention to you in case you've encountered this, cardId has the card's ID immediately on render on the dashboard, but it starts as null when used on the page. I ended up hardcoding our scopes and keys and skipping including the cardId because I wanted to avoid useEffects like you have here.

This code snippet isn't super clear because it uses a custom hook, but I hope you'll get a feel for how my solution works:

const cacheSharedScope = "ppl-loi";
const cacheKeySummary = "summary";

// ...

const PplLettersOfIntentCard = (props) => {
  // ...
  const { status, data } = useScapi({
    endpoint: "letters_of_intent",
    requiresJWT: true,

    // COOL FEATURE ALERT: Load cached data for faster card <-> page <-> card interactions!
    // https://react-query.tanstack.com/guides/initial-query-data
    // Docs: If you configure your query observer with initialData, and no staleTime (the default staleTime: 0), the query will immediately refetch when it mounts:
    // Brandon: This seems like appropriate behavior for a card!
    initialData: () => {
      const { data: cacheData } = getItem({
        key: cacheKeySummary,
        scope: cacheSharedScope
      });
      return cacheData;
    },

    // Brandon created this for being able to call Experience SDK storeItem for caching data
    schoolcraftOnSuccessCallback: (data) => {
      storeItem({ data, key: cacheKeySummary, scope: cacheSharedScope });
    }
  });

  // ...
}
kishba commented 2 years ago

@ellucianBret I forgot to mention you in my first post, and I'm not sure if you'll get a notification if I don't directly @ you. :)

ellucianBret commented 2 years ago

I got an email for both :)

I don't know why I added a wait to those calls. I just got into a habit. I did not notice an issue with it though. Pretty sure it resolves the same way.

I don't remember why I used placeholderData vs initailData. initialData seems more correct for this.

I have noticed the cardId can be undefined on the first render so I try to guard against it. I should but the toolkit team to track it down and "fix" it :)

Bret

ellucianBret commented 2 years ago

@kishba I dove in to remind myself why I did placeholder vs initial data. Initial data will provide the initially "cached" data for the react-query. While placeholder is the data to render until a fetch that is in the background retrieves the latest and greatest.

I am pretty sure what this means is that in your case you are rendering the most recently cached data from local storage, but not updating this with refreshed data.

My typical pattern is to show the most recently cached data, no matter how old, and fetch the latest in the background. If you really want the cached data to show for a specific period of time and don't bother to refresh the data until it expires, then you would use the initialData rather than the placeholderData.

I have removed the waits from the getItems, but I am keeping the placeholder.

Bret

kishba commented 2 years ago

We talked about this outside of GitHub and this from the react-query documentation seems to hold true -- my data is still refetched as soon as the component mounts because it's considered stale!

https://tanstack.com/query/v4/docs/guides/initial-query-data?from=reactQueryV3&original=https://react-query-v3.tanstack.com/guides/initial-query-data#staletime-and-initialdataupdatedat

If you configure your query observer with initialData, and no staleTime (the default staleTime: 0), the query will immediately refetch when it mounts: