toss / suspensive

Manage asynchronous operations, timing, error handling, detecting intersection of elements, and caching easily and declaratively (One of TanStack Query community resources)
https://suspensive.org
MIT License
485 stars 47 forks source link

[Feature]: exporting useTimout? #1094

Open manudeli opened 1 month ago

manudeli commented 1 month ago

Package Scope

@suspensive/react

Description

useTimeout could suspensive interfaces?

Possible Solution

No response

etc.

No response

bigsaigon333 commented 1 month ago

@manudeli, can you tell me more about what "suspensive interfaces" mean?

manudeli commented 1 month ago

I changed description for this repository like below after #1072

AS IS

"Packages to use React Suspense easily"

TO BE

"Manage asynchronous operations, timing, error handling, detecting intersection of elements, and caching easily"

So I thought useTimeout could be "Suspensive interfaces(timing)"

@bigsaigon333 But I want to check whether this vision is good for you too.

bigsaigon333 commented 1 month ago

What a nice discussion! I read #1072 thoroughly and agree with this description 100%.

"Manage asynchronous operations, timing, error handling, detecting intersection of elements, and caching easily"

However, I wanted to add some words after easily, which is and declaratively. The useTimeout hook can be provided by any kind of utility package, and in fact, react-use already provides a useTimeout hook.

I believe that we can provide not only any hook related to asynchronous operations but also declarative React components corresponding to those hooks. The relationship between useSuspenseQuery and <SuspenseQuery /> is a good example.

useTimeout -> <Timeout /> (or <Delay /> may be enough ...?)

manudeli commented 1 month ago

However, I wanted to add some words after easily, which is and declaratively

I added it now! thanks for your feedback!

manudeli commented 1 month ago

I believe that we can provide not only any hook related to asynchronous operations but also declarative React components corresponding to those hooks. The relationship between useSuspenseQuery and is a good example.

useTimeout -> (or may be enough ...?)

I didn't think about <Timeout />, Cool, but I'm curious implementation of . Let's continue to discuss together how we make it and why we need to support it.

bigsaigon333 commented 1 month ago

Well, I just thought using use api from React!

  1. useTimeout will use a Promise that resolves when a specified timeout has elapsed.
  2. <Timeout /> will use use API to expose the promise-based behaviour within a declarative component.

This below is just pseudo code.

import { use } from 'react';

function createTimeoutPromise(duration) {
  return new Promise(resolve => setTimeout(resolve, duration));
}

function Timeout({ duration, children, fallback }) {
  use(createTimeoutPromise(duration)); // Suspend until promise resolves
  return <>{children}</>; // Render children after the timeout
}

I'm trying to understand where a component would be particularly useful, but I haven't come across a specific example that demonstrates its necessity. 😭 Using a declaratively would be nice for me, and I’m pretty sure I would use it quite often, but I doubt others would feel the same way.

What do you think, @manudeli?

manudeli commented 1 month ago

I'm trying to understand where a component would be particularly useful, but I haven't come across a specific example that demonstrates its necessity. 😭

Sadly, in my opinion, It's the most important thing that finding example use cases!

import { use } from 'react';

function createTimeoutPromise(duration) {
  return new Promise(resolve => setTimeout(resolve, duration));
}

function Timeout({ duration, children, fallback }) {
  use(createTimeoutPromise(duration));
  return <>{children}</>;
}

Good idea, but I think this will make circular suspending on render.

  1. Timeout render
  2. createTimeoutPromise will be executed
  3. use(promise) will make suspending (throw this pending promise)
  4. Suspense will try re-render Timeout
  5. createTimeoutPromise will be executed
  6. use(promise) will make suspending (throw this pending promise)
  7. ... (infinite loop)

I fixed it as below. but it have problem that we cannot control timing promise start time

import { use } from 'react';

function createTimeoutPromise(duration) {
  return new Promise(resolve => setTimeout(resolve, duration));
}

const promiseTimeout = createTimeoutPromise(duration) // single source of promise should be guaranteed to suspend only one time

function Timeout({ duration, children, fallback }) {
  use(promiseTimeout);
  return <>{children}</>;
}

So we can make it as cache on mount using @tanstack/react-query or @suspensive/cache to cache promise to suspend only one time on mount

import { useCache } from '@suspensive/cache';

function createTimeoutPromise(duration) {
  return new Promise(resolve => setTimeout(resolve, duration));
}

function TimeoutOnlyOneTime({ duration, children, fallback }) {
  useCache({
    cacheKey: ["cached"],
    cacheFn: () => createTimeoutPromise(duration)
  }) // only one time make promise on mount of TimeoutOnlyOneTime
  return <>{children}</>; // Render children after the timeout
}