Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Svelte suspense (request for comments) #3203

Closed
@brucou

Description

@brucou

After readingthis issue, I came up with aSuspense component for Svelte, replicating the behaviour ofReact Suspense. No React Cache, no throwing promises, no modifying your component to fit a use case, just Svelte component composition. A demo is available in thecorresponding GitHub repository. Note that I could not have the demo running in Svelte REPL due to some issues with loading theaxios package.

svelte suspense demo

The behaviour of the Suspense component is implemented with theKingly state machine library. The summary of 'findings' can be foundhere. For info, here is the underlying state machine specifying the suspense behaviour:

suspense machine

The demo showcases the API and I will quickly illustrate it here. The demo consists of loading a gallery of images. The suspense functionality is applied twice: when fetching the remote data containing the image URLs, and then for each image which is subsequently downloaded. While the remote data is fetched, a spinner will display if fetching takes more than a configurable time. Similarly, images placeholder will also display a spinner if downloading the image takes more than a configurable time.

Firstly, the suspense functionality for the remote data fetching is implemented as follows:

<script> ... a bunch of imports  const iTunesUrl = `https://itunes.apple.com/in/rss/topalbums/limit=100/json`;  function fetchAlbums(intents){const{done, failed}=intents;axios.get(iTunesUrl).then(res=>res.data.feed.entry).then(done).catch(failed)}</script><divclass="app"><Header/><divclass="albums"><Suspensetask={fetchAlbums}let:data={albums}timeout=10><divslot="fallback"class="album-img"><imgalt="loading"src="https://media.giphy.com/media/y1ZBcOGOOtlpC/200.gif"/></div><divslot="error"class="album-img"><h1>ERROR!</h1></div><LazyLoadContainer>{#ifalbums}{#eachalbumsasalbum,i}<LazyLoadid="{i}"><Album{album}/></LazyLoad>{/each}{/if}</LazyLoadContainer></Suspense></div></div>

Note that the fetch task and minimum time (timeout) before displaying the spinner is passed as parameters of theSuspense component, while the fetched data is exposed to the slot component through thedata property. Note also how the fetching function is passed thedone andfailed callback to signal successful completion or error of the remote fetching.

The fallback slot is displayed when the timeout is expired. The error slot is displayed when fetching the data encounters an error.

Secondly, theAlbum component suspense functionality is implemented as follows:

<ulclass="album"><liclass="album-item"><Suspenselet:intents={{done, failed}}timeout=0><divslot="fallback"class="album-img"><imgalt="loading"src="https://media.giphy.com/media/y1ZBcOGOOtlpC/200.gif"/></div><ahref={link}target="blank"class="link"><imgclass="album-img"on:load={done}src={image}alt={'itunes'+Math.random()}/></a></Suspense></li><liclass="title album-item"><ahref={link}target="blank"class="link">{title.slice(0,20)}..</a></li><liclass="price album-item">Price:{price}</li><liclass="date album-item">Released:{formatDate(date,"MMM Do YY")}</li></ul>

This time theSuspense component passesdone andfailed callbacks to its children slots. When the image is loaded, thedone callback is run.

This works well and I believe the API separates well the suspense functionality or concern from the slots. What we basically have is parent and children components communicating through events, except that the event comes included in the callback. As the demo shows, there is also no issues with nestingSuspense components.

This GitHub issues has two purposes:

  • gettign feedback on the API
  • giving feedback on Svelte slot composition

The first point is more about hearing from you guys.

About the second point:

  • slot composition is a powerful and flexible mechanism, specially in conjunction with scoped slots
  • however, a few things would be nice:
    1. being able to operate on the slot as if it were a regular html element. This mean the ability to style a slot with classes or possibly other attributes (<slot class='...' style='...'> </slot>). Add some extra attributed to covergeneric needs, i.e. needs that areindependent of the content of the slot. To implement the suspense functionality I had to resort to hide the default slot withdisplay:none. Unfortunately to do that I had to wrap the slot around adiv element, which can have side effects depending on the surrounding css. A syntax like<slot show={show}> </slot> would have been ideal. After thinking some more, I think that slot cannot be considered as regular HTML elements but as an abstract container for such elements. The operations allowed on slots should be operation on the container, not on the elements directly. Styling or adding classes an abstract container does not carry an obvious meaning, as the container is not a DOM abstraction. The current operations I see existing on the container areget (used internally by Svelte to get the slot content),show could be another one. The idea is that if you have a Container type, and a Element type, your container isC<E>. If you do operations that are independents ofE, you can do only a few things like use E (get), ignore E (don't use the slot), repeat E (not sure how useful that would be), conditionally use E (show, of type Maybe). Using any knowledge about theE I think leads to crossing abstraction boundaries which might not be a good thing future-wise.
    2. having slots on component just like if components were regular elements
    3. having dynamic slots. In theSuspense component, I useif/then/else to pick up the slot to display, which works fine (see code below). It would be nice however to have<slot name={expression ...}>:
{#if stillLoading }<slotname="fallback"dispatch={next}intents={intents}></slot>{:else if errorOccurred }<slotname="error"dispatch={next}intents={intents}data={data}></slot>{:else if done }<slotdispatch={next}intents={intents}data={data}></slot>{/if}<divclass="incognito"><slotdispatch={next}intents={intents}></slot></div>

I am not really strong about the dynamic slots. It might add some complexity that may be best avoided for now. The first and second point however I believe are important for abstraction and composition purposes. My idea is to use Svelte components which only implement behaviour and delegate UI to their children slots (similar toVue renderless components). Done well, with this technique you end up with logic in logic components, and the view in stateless ui elements.

The technique has additionally important testing benefits (the long read ishere).

For instance the behaviour of theSuspense state machine can betested independently of Svelte - and the browser, and with using a state machine, tests can even be automatically generated (finishing that up at the moment). Last, the state machine library can compile itself away just like Svelte does :-) (the implementation is actually using thecompiled machine).

About testing stateless components, Storybook can be set to good purpose. What do you Svelte experts and non experts think? I am pretty new with Svelte by the way, so if there is any ways to do what I do better, also please let me know.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions


      [8]ページ先頭

      ©2009-2025 Movatter.jp