These are chat archives for jdubray/sam

3rd
Nov 2018
Fred Daoud
@foxdonut
Nov 03 2018 14:00

One of the nice use cases for React Suspense is when you go to a page that needs to fetch data.

While the data is loading, you'd like to show a spinner. Once the data is loaded, you refresh the view and display the data.

But, in the case where the fetching of data is fast, you're only showing the spinner for a fraction of a second, barely enough for the user to see it, but, more problematic, definitely enough for the user to notice a "flicker".

Hence, in the latter case, you'd like to not show the spinner at all. In other words, only show the spinner if loading takes more than a certain amount of time, for example 300 ms.

React Suspense provides APIs for this, but, as I am certain @jdubray already knows, this is simple to achieve with SAM without needing any special API from the view library.

I've started working on this in the realworld Meiosis/SAM example (work in progress), here is my approach:

  • When you navigate to a page, the NextAction immediately triggers a delayed proposal (300 ms) with a loading flag.
  • The same NextAction also immediately triggers the action that loads the data from the server.
  • If the data has already loaded, the Acceptor refuses the loading flag proposal.
  • Thus, if the data takes longer than 300 ms, the loading flag proposal is accepted and the spinner is shown.
  • If instead the data takes less than 300 ms, the view displays the data and the loading flag proposal that arrives later is refused.

This approach is working well so far.

What do you think?

NextAction:

export const nextAction = update => (_model, patch) => {
  if (patch.pageId === HomePage) {
    setTimeout(() => update({ loading: HomePage }), 300)

    helpers.loadArticles(patch.params).then(
      data => update(Object.assign({ loading: null }, data))
    )
  }
}

Acceptor:

export const accept = (model, patch) => {
  if (patch.loading === HomePage && model.articles) {
    return null
  }
  return patch
}
View:
  model.articles
    ? articles(model)
    : ["div", { style: "height: 2000px" },
      model.loading ? ["img", { src: "/assets/loading.gif" }] : null]