From ee19a7a736db48869aa6cb9d7d7d52d6fda003b8 Mon Sep 17 00:00:00 2001 From: Vladislav Lipatov Date: Tue, 23 Jul 2024 11:15:31 +0700 Subject: [PATCH 1/2] Support temporary `latest` field for `createAsync` and `createAsyncStore` --- README.md | 8 ++++++++ src/data/createAsync.ts | 42 +++++++++++++++++++++++++++++++++-------- 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index cb042802f..2dc19ff98 100644 --- a/README.md +++ b/README.md @@ -495,11 +495,19 @@ This is light wrapper over `createResource` that aims to serve as stand-in for a const user = createAsync((currentValue) => getUser(params.id)) ``` +It also preserves `latest` field from `createResource`. Note that it will be removed in the future. + +```jsx +const user = createAsync((currentValue) => getUser(params.id)) +return

{user.latest.name}

; +``` + Using `cache` in `createResource` directly won't work properly as the fetcher is not reactive and it won't invalidate properly. ### `createAsyncStore` Similar to `createAsync` except it uses a deeply reactive store. Perfect for applying fine-grained changes to large model data that updates. +It also supports `latest` field which will be removed in the future. ```jsx const todos = createAsyncStore(() => getTodos()); diff --git a/src/data/createAsync.ts b/src/data/createAsync.ts index e3218da11..1b85976d9 100644 --- a/src/data/createAsync.ts +++ b/src/data/createAsync.ts @@ -5,6 +5,16 @@ import { type Accessor, createResource, sharedConfig, type Setter, untrack } fro import { createStore, reconcile, type ReconcileOptions, unwrap } from "solid-js/store"; import { isServer } from "solid-js/web"; +/** + * As `createAsync` and `createAsyncStore` are wrappers for `createResource`, + * this type allows to support `latest` field for these primitives. + * It will be removed in the future. + */ +type AccessorWithLatest = { + (): T; + latest: T; +} + export function createAsync( fn: (prev: T) => Promise, options: { @@ -12,7 +22,7 @@ export function createAsync( initialValue: T; deferStream?: boolean; } -): Accessor; +): AccessorWithLatest; export function createAsync( fn: (prev: T | undefined) => Promise, options?: { @@ -20,7 +30,7 @@ export function createAsync( initialValue?: T; deferStream?: boolean; } -): Accessor; +): AccessorWithLatest; export function createAsync( fn: (prev: T | undefined) => Promise, options?: { @@ -28,7 +38,7 @@ export function createAsync( initialValue?: T; deferStream?: boolean; } -): Accessor { +): AccessorWithLatest { let resource: () => T; let prev = () => !resource || (resource as any).state === "unresolved" ? undefined : (resource as any).latest; [resource] = createResource( @@ -36,7 +46,15 @@ export function createAsync( v => v, options as any ); - return () => resource(); + + const resultAccessor: AccessorWithLatest = (() => resource()) as any; + Object.defineProperty(resultAccessor, 'latest', { + get() { + return (resource as any).latest; + } + }) + + return resultAccessor; } export function createAsyncStore( @@ -47,7 +65,7 @@ export function createAsyncStore( deferStream?: boolean; reconcile?: ReconcileOptions; } -): Accessor; +): AccessorWithLatest; export function createAsyncStore( fn: (prev: T | undefined) => Promise, options?: { @@ -56,7 +74,7 @@ export function createAsyncStore( deferStream?: boolean; reconcile?: ReconcileOptions; } -): Accessor; +): AccessorWithLatest; export function createAsyncStore( fn: (prev: T | undefined) => Promise, options: { @@ -65,7 +83,7 @@ export function createAsyncStore( deferStream?: boolean; reconcile?: ReconcileOptions; } = {} -): Accessor { +): AccessorWithLatest { let resource: () => T; let prev = () => !resource || (resource as any).state === "unresolved" ? undefined : unwrap((resource as any).latest); [resource] = createResource( @@ -76,7 +94,15 @@ export function createAsyncStore( storage: (init: T | undefined) => createDeepSignal(init, options.reconcile) } as any ); - return () => resource(); + + const resultAccessor: AccessorWithLatest = (() => resource()) as any; + Object.defineProperty(resultAccessor, 'latest', { + get() { + return (resource as any).latest; + } + }) + + return resultAccessor; } function createDeepSignal(value: T | undefined, options?: ReconcileOptions) { From 6531a764403c1048be9f709a790551106f4c0cd5 Mon Sep 17 00:00:00 2001 From: Vladislav Lipatov Date: Tue, 23 Jul 2024 16:31:01 +0700 Subject: [PATCH 2/2] Export `AccessorWithLatest` type --- src/data/createAsync.ts | 2 +- src/data/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/data/createAsync.ts b/src/data/createAsync.ts index 1b85976d9..7aecb6275 100644 --- a/src/data/createAsync.ts +++ b/src/data/createAsync.ts @@ -10,7 +10,7 @@ import { isServer } from "solid-js/web"; * this type allows to support `latest` field for these primitives. * It will be removed in the future. */ -type AccessorWithLatest = { +export type AccessorWithLatest = { (): T; latest: T; } diff --git a/src/data/index.ts b/src/data/index.ts index 9c3809869..ab232b211 100644 --- a/src/data/index.ts +++ b/src/data/index.ts @@ -1,4 +1,4 @@ -export { createAsync, createAsyncStore } from "./createAsync.js"; +export { createAsync, createAsyncStore, type AccessorWithLatest } from "./createAsync.js"; export { action, useSubmission, useSubmissions, useAction, type Action } from "./action.js"; export { cache, revalidate, type CachedFunction } from "./cache.js"; export { redirect, reload, json } from "./response.js";