Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

solidjs style computeds & async computeds #188

Open
btakita opened this issue Apr 28, 2023 · 8 comments
Open

solidjs style computeds & async computeds #188

btakita opened this issue Apr 28, 2023 · 8 comments

Comments

@btakita
Copy link
Contributor

btakita commented Apr 28, 2023

Solidjs signals use inline dependencies, removing the redundancy of declaring the dependency atoms when the computed is defined. I think we can lazily add dependencies when the computed function is executed.

Perhaps it could look something like:

const base = atom(0)
const dependency = computed(()=>base() + 1)
console.info({ dependency: dependency() })
dependency.unbind()

I think it would work even if a dependency is not reached in a particular execution...

const base0 = atom(0)
const base1 = atom('foobar')
const dependency = computed(()=>{
  if (base0() >= 2) return base1()
  return 'baz'
})
console.info(dependency()) // 'baz'
// dependency does not yet listen to base1
base0.set(1)
console.info(dependency()) // 'baz'
// dependency now listens to base1
base0.set(2)
console.info(dependency()) // 'foobar'
dependency.unbind()

It could also work for async as well. Given that the get function argument is necessary to push the caller to the caller stack to track the which computed will be subscribing to the dependency. Solidjs memos are synchronous, so there is no equivalent.

const base = atom(0)
const retry_id = atom(0)
const dependency = computed(async (get, set)=>{
  set({ loading: true })
  const response = await fetch(`https://my-api/api?id=${get(base)}`)
  const payload = await response.json()
  set({
    payload,
    retry_id: get(retry_id)
  })
})
console.info({ dependency: dependency() }) // { loading: true }
// wait for first response
console.info({ dependency: dependency() }) // { payload: { ... }, retry_id: 0 }
retry_id.set(retry_id.get() + 1)
// wait for second response
console.info({ dependency: dependency() }) // { payload: { ... }, retry_id: 1 }
dependency.unbind()

or get could be used as a sort of self argument

const base = atom(0)
const retry_id = atom(0)
const dependency = computed(async (self, set)=>{
  set({ loading: true })
  const response = await fetch(`https://my-api/api?id=${base.get(self)}`)
  const payload = await response.json()
  set({
    payload,
    retry_id: retry_id.get(self)
  })
})

This could also be a separate library which imports nanostores.

@ai
Copy link
Member

ai commented Apr 29, 2023

It looks interesting. Can somebody make an PR? We need to check the size of this feature.

@btakita
Copy link
Contributor Author

btakita commented Apr 29, 2023

I can work on a PR when I have some free time, which will be at least 2 weeks from now. At the very least, this can be a separate package, but it may be small enough to include in this project. To anyone reading this who wants to work on this, don't let this message dissuade you from creating a patch.

btakita added a commit to btakita/nanostores that referenced this issue Jun 12, 2023
atom: is a function which takes an optional autolisten & returns .get()

	+ .a: active autolisten when atom was instantiated

computed:

	+ autolisten:
		.set: sets computed & does not set undoValue
		.save: sets computed & sets undoValue
		.undo: sets computed to undoValue
		.on: hook which runs callback onMount
		.off: hook which runs callback onUnmount & before the computed cb being called
	+ async support

size-limit:

	Atom: + 9 B
	All: + 259 B
btakita added a commit to btakita/nanostores that referenced this issue Jun 12, 2023
atom: is a function which takes an optional autolisten & returns .get()

	+ .a: active autolisten when atom was instantiated

computed:

	+ autolisten:
		.set: sets computed & does not set undoValue
		.save: sets computed & sets undoValue
		.undo: sets computed to undoValue
		.on: hook which runs callback onMount
		.off: hook which runs callback onUnmount & before the computed cb being called
	+ async support

size-limit:

	Atom: + 9 B
	All: + 259 B
btakita added a commit to btakita/nanostores that referenced this issue Jun 12, 2023
atom: is a function which takes an optional autosubscribe function & returns .get()

	+ .a: active autosubscribe when atom was instantiated

computed:

	+ autosubscribe:
		.set: sets computed & does not set undoValue
		.save: sets computed & sets undoValue
		.undo: sets computed to undoValue
		.on: hook which runs callback onMount
		.off: hook which runs callback onUnmount & before the computed cb being called
	+ async support

size-limit:

	Atom: + 9 B
	All: + 258 B
btakita added a commit to btakita/nanostores that referenced this issue Jun 12, 2023
atom: is a function which takes an optional autosubscribe function & returns .get()

	+ .a: active autosubscribe when atom was instantiated

computed:

	+ autosubscribe:
		.set: sets computed & does not set undoValue
		.save: sets computed & sets undoValue
		.undo: sets computed to undoValue
		.on: hook which runs callback onMount
		.off: hook which runs callback onUnmount & before the computed cb being called
	+ async support

size-limit:

	Atom: + 9 B
	All: + 258 B
btakita added a commit to btakita/nanostores that referenced this issue Jun 12, 2023
atom: is a function which takes an optional autosubscribe function & returns .get()

	+ .a: active autosubscribe when atom was instantiated

computed:

	+ autosubscribe:
		.set: sets computed & does not set undoValue
		.save: sets computed & sets undoValue
		.undo: sets computed to undoValue
		.on: hook which runs callback onMount
		.off: hook which runs callback onUnmount & before the computed cb being called
	+ async support

size-limit:

	Atom: + 9 B
	All: + 258 B
btakita added a commit to btakita/nanostores that referenced this issue Jun 12, 2023
atom: is a function which takes an optional autosubscribe function & returns .get()

	+ .a: active autosubscribe when atom was instantiated

computed:

	+ autosubscribe:
		.set: sets computed & does not set undoValue
		.save: sets computed & sets undoValue
		.undo: sets computed to undoValue
		.on: hook which runs callback onMount
		.off: hook which runs callback onUnmount & before the computed cb being called
	+ async support

size-limit:

	Atom: + 9 B
	All: + 258 B
btakita added a commit to btakita/nanostores that referenced this issue Jun 12, 2023
atom: is a function which takes an optional autosubscribe function & returns .get()

	+ .a: active autosubscribe when atom was instantiated

computed:

	+ autosubscribe:
		.set: sets computed & does not set undoValue
		.save: sets computed & sets undoValue
		.undo: sets computed to undoValue
		.on: hook which runs callback onMount
		.off: hook which runs callback onUnmount & before the computed cb being called
	+ async support

size-limit:

	Atom: + 9 B
	All: + 258 B
btakita added a commit to btakita/nanostores that referenced this issue Jun 12, 2023
atom: is a function which takes an optional autosubscribe function & returns .get()

	+ .a: active autosubscribe when atom was instantiated

computed:

	+ autosubscribe:
		.set: sets computed & does not set undoValue
		.save: sets computed & sets undoValue
		.undo: sets computed to undoValue
		.on: hook which runs callback onMount
		.off: hook which runs callback onUnmount & before the computed cb being called
	+ async support

size-limit:

	Atom: + 9 B
	All: + 258 B
btakita added a commit to btakita/nanostores that referenced this issue Jun 12, 2023
atom: is a function which takes an optional autosubscribe function & returns .get()

	+ .a: active autosubscribe when atom was instantiated

computed:

	+ autosubscribe:
		.set: sets computed & does not set undoValue
		.save: sets computed & sets undoValue
		.undo: sets computed to undoValue
		.on: hook which runs callback onMount
		.off: hook which runs callback onUnmount & before the computed cb being called
	+ async support

size-limit:

	Atom: + 9 B
	All: + 258 B
btakita added a commit to btakita/nanostores that referenced this issue Jun 12, 2023
atom: is a function which takes an optional autosubscribe function & returns .get()

	+ .a: active autosubscribe when atom was instantiated

computed:

	+ autosubscribe:
		.set: sets computed & does not set undoValue
		.save: sets computed & sets undoValue
		.undo: sets computed to undoValue
		.on: hook which runs callback onMount
		.off: hook which runs callback onUnmount & before the computed cb being called
	+ async support

size-limit:

	Atom: + 9 B
	All: + 258 B
btakita added a commit to btakita/nanostores that referenced this issue Jun 12, 2023
atom: is a function which takes an optional autosubscribe function & returns .get()

	+ .a: active autosubscribe when atom was instantiated

computed:

	+ autosubscribe:
		.set: sets computed & does not set undoValue
		.save: sets computed & sets undoValue
		.undo: sets computed to undoValue
		.on: hook which runs callback onMount
		.off: hook which runs callback onUnmount & before the computed cb being called
	+ async support

size-limit:

	Atom: + 9 B
	All: + 258 B
btakita added a commit to btakita/nanostores that referenced this issue Jun 12, 2023
atom: is a function which takes an optional autosubscribe function & returns .get()

	+ .a: active autosubscribe when atom was instantiated

computed:

	+ autosubscribe:
		.set: sets computed & does not set undoValue
		.save: sets computed & sets undoValue
		.undo: sets computed to undoValue
		.on: hook which runs callback onMount
		.off: hook which runs callback onUnmount & before the computed cb being called
	+ async support

size-limit:

	Atom: + 9 B
	All: + 258 B
btakita added a commit to btakita/nanostores that referenced this issue Jun 14, 2023
atom: is a function which takes an optional autosubscribe function & returns .get()

	+ .a: active autosubscribe when atom was instantiated

computed:

	+ autosubscribe:
		(): returns computed store's value
		(store: AnyStore): computed store autosubscribes to the given store
		(cb: () => any): runs the cb & autosubscribes to any called store functions within the cb
		.off(cb): hook which runs callback onUnmount & before the computed cb being called
		.on(cb): hook which runs callback onMount
		.save(): sets computed & sets undoValue
		.set(newValue): sets computed & does not set undoValue
		.stale(): returns true if computed store's cb was called again after call associated with the autosubscribe
		.undo: sets computed to undoValue
		a stale autosubscribe has some of it's methods deactivated:
			.off
			.on
			.save
			.set
			.undo
	+ async support

size-limit:

	Atom: + 9 B
	All: + 299 B
btakita added a commit to btakita/nanostores that referenced this issue Jun 14, 2023
atom: is a function which takes an optional autosubscribe function & returns .get()

	+ .a: active autosubscribe when atom was instantiated

computed:

	+ autosubscribe:
		(): returns computed store's value
		(store: AnyStore): computed store autosubscribes to the given store
		(cb: () => any): runs the cb & autosubscribes to any called store functions within the cb
		.off(cb): hook which runs callback onUnmount & before the computed cb being called
		.on(cb): hook which runs callback onMount
		.save(): sets computed & sets undoValue
		.set(newValue): sets computed & does not set undoValue
		.stale(): returns true if computed store's cb was called again after call associated with the autosubscribe
		.undo: sets computed to undoValue
		a stale autosubscribe has some of it's methods neutered:
			.off
			.on
			.save
			.set
			.undo
	+ async support

size-limit:

	Atom: + 9 B
	All: + 299 B
btakita added a commit to btakita/nanostores that referenced this issue Jun 14, 2023
atom: is a function which takes an optional autosubscribe function & returns .get()

	+ .a: active autosubscribe when atom was instantiated

computed:

	+ autosubscribe:
		(): returns computed store's value
		(store: AnyStore): computed store autosubscribes to the given store
		(cb: () => any): runs the cb & autosubscribes to any called store functions within the cb
		.off(cb): hook which runs callback onUnmount & before the computed cb being called
		.on(cb): hook which runs callback onMount
		.save(): sets computed & sets undoValue
		.set(newValue): sets computed & does not set undoValue
		.stale(): returns true if computed store's cb was called again after call associated with the autosubscribe
		.undo: sets computed to undoValue
		a stale autosubscribe has some of it's methods neutered:
			.off
			.on
			.save
			.set
			.undo
	+ async support

size-limit:

	Atom: + 9 B
	All: + 295 B
btakita added a commit to btakita/nanostores that referenced this issue Jun 16, 2023
…tosubscribe (nanostores#188)

atom: is a function which takes an optional autosubscribe function & returns .get()

	+ .a: active autosubscribe when atom was instantiated

computed:

	+ autosubscribe:
		(): returns computed store's value
		(store: AnyStore): computed store autosubscribes to the given store
		(cb: () => any): runs the cb & autosubscribes to any called store functions within the cb
		.off(cb): hook which runs callback onUnmount & before the computed cb being called
		.on(cb): hook which runs callback onMount
		.save(): sets computed & sets undoValue
		.set(newValue): sets computed & does not set undoValue
		.stale(): returns true if computed store's cb was called again after call associated with the autosubscribe
		.undo: sets computed to undoValue
		a stale autosubscribe has some of it's methods neutered:
			.off
			.on
			.save
			.set
			.undo
	+ async support

size-limit:

	Atom: + 9 B
	All: + 295 B
@dtinth
Copy link

dtinth commented Jun 28, 2023

For nanostores, I implemented a userland solution here: https://github.com/dtinth/nanostores-computed-dynamic

The API looks like this:

const $base = atom(0)

const $derived = computedDynamic(use => use($base) + 1)
// `use($store)` is like `$store.get()` but it also tracks the dependency.

It is possible to use use inside conditionals:

const $shape = atom('circle')
const $radius = atom(4)
const $width = atom(2)
const $height = atom(3)

const $area = computedDynamic((use) => {
  if (use($shape) === 'circle') {
    return Math.PI * use($radius) ** 2
  }
  if (use($shape) === 'rectangle') {
    return use($width) * use($height)
  }
})

// If $shape changes from 'circle' to 'rectangle', then
// $radius is unsubscribed, and $width and $height are subscribed instead.

…or loops (just like React’s use special hook!):

const $users = map({
  u1: { name: 'John', feeling: '😀' },
  u2: { name: 'Mary', feeling: '😀' },
})
const $onlineUserIds = atom(['u1'])

const $userById = (id) => computed($users, (users) => users[id])
const $onlineUsers = computedDynamic((use) => {
  const onlineUserIds = use($onlineUserIds)
  return onlineUserIds.map((id) => use($userById(id)))
})

Subscriptions are created and destroyed dynamically as the dependencies change across recomputations.

It is also possible to use a store to switch between other stores!

const $a = atom('a1')
const $b = atom('b1')
const $switcher = atom($a)
const $c = computedDynamic((use) => {
  return use(use($switcher))
})

It is not thoroughly tested yet though.

I think it’d be great if this is built in to nanostores.

Having the ability to create computed store with dynamic dependencies is important for performance. For example, if you have a $users store (map from user ID to profile) and an $onlineUserIds store (array of IDs of online users) and you want to create an $onlineUsers store that is computed from both, ideally, changes to $users not listed in $onlineUsers should not trigger recalculation. This is currently not possible with computed because all dependencies have to be specified upfront. (I elaborated about this problem in more details in this StackOverflow answer about reselect, which uses a similar syntax with nanostores’ computed.)

@btakita
Copy link
Contributor Author

btakita commented Jun 28, 2023

Hey @dtinth! I created a PR which adds autosubscribe & async support to computed. So your example would be supported as you wrote it using computed or since atoms are functions, can be called directly with autosubscribe in synchronous handlers. An added benefit to an atom being a function is the return type can be inferred. Function arguments (e.g. the use argument) cannot have generic types so the return type cannot be inferred.

const $shape = atom('circle')
const $radius = atom(4)
const $width = atom(2)
const $height = atom(3)

const $area = computed(() => {
  if ($shape() === 'circle') {
    return Math.PI * $radius() ** 2
  }
  if ($shape() === 'rectangle') {
    return $width() * $height()
  }
})
const $a = atom('a1')
const $b = atom('b1')
const $switcher = atom($a)
const $c = computed(() => {
  return $switcher()()
})

There is also support for async computeds. Re-entrancy is also supported, meaning if a callback is run before a prior callback completes, the prior callback will be marked as "stale". An example:

let $personId = atom(0)
computed(async use => {
  let person = await fetch(`https://api.com/people/${$personId()}`).then(response => response.json())
  if (use.stale()) return // stop run if the cb is stale
  let messages = await fetch(`https://api.com/people/${$personId(use)}/messages`).then(response => response.json())
  return { person, messages } // This will not be saved if the cb is stale
})

I added tests & have deployed this PR to a medium-sized project & all seems well! @ai, have you had a chance to test the PR?

@dtinth
Copy link

dtinth commented Jun 28, 2023

@btakita Thanks for the pointer. I took a quick look at the PR.

  • I am impressed with the implementation of async. I think it would be useful, especially that you handled the "stale" case.
  • I’m not a fan of using () to read the store value though, because then there are 2 ways to read the value. I am afraid that it could lead to inconsistent API usage. Regarding the TypeScript inference issue, I think it’s possible to do this: cb: (get: <T>(atom: ReadonlyStore<T>)=>T) => Value (this is what I do in my lib).

@btakita
Copy link
Contributor Author

btakita commented Jun 28, 2023

I am impressed with the implementation of async. I think it would be useful, especially that you handled the "stale" case.

Thank you! There are some additional use cases which includes Event binding & unbinding and Helper functions & Private stores.

The helper functions on Autosubscribe (use) are:

  • (): Value: get the current value of the computed within the cb
  • <V>(atom: ReadableAtom<V>): V: The computed will autosubscribe to the atom, returning the current value of the atom
  • on(fn: () => any): Autosubscribe<Value>: callback when the computed is first listened to. Useful for binding to events within the computed cb
  • off(fn: () => any): Autosubscribe<Value>: callback when all listeners are unbound. Useful for unbinding to events within the computed cb
  • set(intermediateValue: Value): Autosubscribe<Value>: sets an intermediate value of the computed. Useful for intermediate async states, such as loading, which can be rolled back to the last saved value using .undo()
  • save(newValue: Value): Autosubscribe<Value>: sets the computed value & undoValue. Note that when the computed's cb completes, the value is saved.
  • undo(): Autosubscribe<Value>: resets the computed back to the last saved value. Useful to rolling back to the previously saved value when an async cb is stale.
  • stale(): boolean: returns true when the computed's async cb is called while the current run of the async cb is not complete

I’m not a fan of using () to read the store value though, because then there are 2 ways to read the value. I am afraid that it could lead to inconsistent API usage. Regarding the TypeScript inference issue, I think it’s possible to do this: cb: (get: (atom: ReadonlyStore)=>T) => Value (this is what I do in my lib).

First of all, thank you for the type inference tip. I can confirm that it works & now the use function correctly infers the return type.

I can see your point re: keeping the interface small. Another benefit to not having an atom as a function is that the autosubscriberStack & autosubscriber are not necessary. This would reduce the size of the implementation & simplify the api. I agree that it is more appropriate to have use be solely responsible for autosubscription in the nanostores library.

I personally like having atom be a function because it reduces the noise of overusing use, which is particularly helpful when multiple stores are autosubscribed in close proximity. I also have multiple libraries with functions that extend atom & computed. In this case, I can extend atom & computed via a Proxy to give an atom a function interface & not having to pass around use would be helpful.

I prefer having the api sugar to improve readability of my own projects, but I also would rather not have to document & maintain multiple ways of autosubscribing in the nanostores project...I also have the sense that others, including @TrySound would also prefer the simplicity of only supporting use and the library size would also be less impacted.

So I appreciate your comments & am adjusting the PR accordingly. If anyone wants to use the atom function interface, I will extract a separate library (name TBD) which does this.

btakita added a commit to btakita/nanostores that referenced this issue Jun 28, 2023
…tosubscribe (nanostores#188)

atom: is a function which takes an optional autosubscribe function & returns .get()

	+ .a: active autosubscribe when atom was instantiated

computed:

	+ autosubscribe:
		(): returns computed store's value
		(store: AnyStore): computed store autosubscribes to the given store
		(cb: () => any): runs the cb & autosubscribes to any called store functions within the cb
		.off(cb): hook which runs callback onUnmount & before the computed cb being called
		.on(cb): hook which runs callback onMount
		.save(): sets computed & sets undoValue
		.set(newValue): sets computed & does not set undoValue
		.stale(): returns true if computed store's cb was called again after call associated with the autosubscribe
		.undo: sets computed to undoValue
		a stale autosubscribe has some of it's methods neutered:
			.off
			.on
			.save
			.set
			.undo
	+ async support

Thanks to @dtinth for autosubscribe function type inference which he implemented in https://github.com/dtinth/nanostores-computed-dynamic

size-limit:

	Atom: + 9 B
	All: + 295 B
btakita added a commit to btakita/nanostores that referenced this issue Jun 28, 2023
…tosubscribe (nanostores#188)

atom: is a function which takes an optional autosubscribe function & returns .get()

	+ .a: active autosubscribe when atom was instantiated

computed:

	+ autosubscribe:
		(): returns computed store's value
		(store: AnyStore): computed store autosubscribes to the given store
		(cb: () => any): runs the cb & autosubscribes to any called store functions within the cb
		.off(cb): hook which runs callback onUnmount & before the computed cb being called
		.on(cb): hook which runs callback onMount
		.save(): sets computed & sets undoValue
		.set(newValue): sets computed & does not set undoValue
		.stale(): returns true if computed store's cb was called again after call associated with the autosubscribe
		.undo: sets computed to undoValue
		a stale autosubscribe has some of it's methods neutered:
			.off
			.on
			.save
			.set
			.undo
	+ async support

Thanks to @dtinth for autosubscribe function type inference which he implemented in https://github.com/dtinth/nanostores-computed-dynamic

size-limit:

	Atom: + 9 B
	All: + 295 B
btakita added a commit to btakita/nanostores that referenced this issue Jun 28, 2023
…tosubscribe (nanostores#188)

atom: is a function which takes an optional autosubscribe function & returns .get()

	+ .a: active autosubscribe when atom was instantiated

computed:

	+ autosubscribe:
		(): returns computed store's value
		(store: AnyStore): computed store autosubscribes to the given store
		(cb: () => any): runs the cb & autosubscribes to any called store functions within the cb
		.off(cb): hook which runs callback onUnmount & before the computed cb being called
		.on(cb): hook which runs callback onMount
		.save(): sets computed & sets undoValue
		.set(newValue): sets computed & does not set undoValue
		.stale(): returns true if computed store's cb was called again after call associated with the autosubscribe
		.undo: sets computed to undoValue
		a stale autosubscribe has some of it's methods neutered:
			.off
			.on
			.save
			.set
			.undo
	+ async support

Thanks to @dtinth for autosubscribe function type inference which he implemented in https://github.com/dtinth/nanostores-computed-dynamic

size-limit:

	Atom: + 9 B
	All: + 295 B
@btakita
Copy link
Contributor Author

btakita commented Jun 28, 2023

I removed the ReadableAtom function api in the btakita/nanostores#issues/188 branch used by the PR 203.

I also have the btakita/nanostores#issues/188-atom-fn available which keeps the ReadableAtom function api.

If we still prefer to have functionless atoms in nanostores, I can extract a new package for the function atoms. Either way, it would be great to have the PR merged.

@ai
Copy link
Member

ai commented Jun 29, 2023

Sorry, I still have no time to review that PR. I need to prepare a talk and PR is very big to review it fast.

I will try to do it in July.

btakita added a commit to btakita/nanostores that referenced this issue Jul 8, 2023
…tosubscribe (nanostores#188)

atom: is a function which takes an optional autosubscribe function & returns .get()

	+ .a: active autosubscribe when atom was instantiated

computed:

	+ autosubscribe:
		(): returns computed store's value
		(store: AnyStore): computed store autosubscribes to the given store
		(cb: () => any): runs the cb & autosubscribes to any called store functions within the cb
		.off(cb): hook which runs callback onUnmount & before the computed cb being called
		.on(cb): hook which runs callback onMount
		.save(): sets computed & sets undoValue
		.set(newValue): sets computed & does not set undoValue
		.stale(): returns true if computed store's cb was called again after call associated with the autosubscribe
		.undo: sets computed to undoValue
		a stale autosubscribe has some of it's methods neutered:
			.off
			.on
			.save
			.set
			.undo
	+ async support

Thanks to @dtinth for autosubscribe function type inference which he implemented in https://github.com/dtinth/nanostores-computed-dynamic

size-limit:

	Atom: - 4 B
	All: + 288 B
btakita added a commit to btakita/nanostores that referenced this issue Jul 8, 2023
…tosubscribe (nanostores#188)

atom: is a function which takes an optional autosubscribe function & returns .get()

	+ .a: active autosubscribe when atom was instantiated

computed:

	+ autosubscribe:
		(): returns computed store's value
		(store: AnyStore): computed store autosubscribes to the given store
		(cb: () => any): runs the cb & autosubscribes to any called store functions within the cb
		.off(cb): hook which runs callback onUnmount & before the computed cb being called
		.on(cb): hook which runs callback onMount
		.save(): sets computed & sets undoValue
		.set(newValue): sets computed & does not set undoValue
		.stale(): returns true if computed store's cb was called again after call associated with the autosubscribe
		.undo: sets computed to undoValue
		a stale autosubscribe has some of it's methods neutered:
			.off
			.on
			.save
			.set
			.undo
	+ async support

Thanks to @dtinth for autosubscribe function type inference which he implemented in https://github.com/dtinth/nanostores-computed-dynamic

size-limit:

	Atom: + 9 B
	All: + 289 B
btakita added a commit to btakita/nanostores that referenced this issue Jul 16, 2023
	BREAKING CHANGE: If the `$computed()` returns a `PromiseLike`, the `PromiseLike`'s `.then()` is called to set `$computed.value`.
    Mitigation: the `$computed()` can return an object wrapping the `PromiseLike`.
		During the synchronous run of the `computed` callback, the `task` is pushed onto the `taskStack`.

Task:

	+ save(newValue: Value): Task<Value>
	+ set(intermediateValue: Value): Task<Value>
	+ stale(): boolean
	+ undo(): Task<Value>

size-limit:

	All: + 78 B
btakita added a commit to btakita/nanostores that referenced this issue Jul 19, 2023
+ BoxTask,task,Task,UnboxTask

atom: is a function which takes an optional autosubscribe function & returns .get()

computed:

	+ task:
		(): returns computed store's value
		(store: AnyStore): computed store autosubscribes to the given store

package.json: description: + SolidJS

Thanks to @dtinth for autosubscribe function type inference which he implemented in https://github.com/dtinth/nanostores-computed-dynamic

size-limit:

	Atom: + 26 B
	All: + 159 B
btakita added a commit to btakita/nanostores that referenced this issue Jul 19, 2023
	BREAKING CHANGE: If the `$computed()` returns a `PromiseLike`, the `PromiseLike`'s `.then()` is called to set `$computed.value`.
    Mitigation: the `$computed()` can return an object wrapping the `PromiseLike`.
		During the synchronous run of the `computed` callback, the `task` is pushed onto the `taskStack`.

Task:

	+ save(newValue: Value): Task<Value>
	+ set(intermediateValue: Value): Task<Value>
	+ stale(): boolean
	+ undo(): Task<Value>

size-limit:

	All: + 78 B
btakita added a commit to btakita/nanostores that referenced this issue Jul 19, 2023
+ BoxTask,task,Task,UnboxTask

atom: is a function which takes an optional autosubscribe function & returns .get()

computed:

	+ task:
		(): returns computed store's value
		(store: AnyStore): computed store autosubscribes to the given store

package.json: description: + SolidJS

Thanks to @dtinth for autosubscribe function type inference which he implemented in https://github.com/dtinth/nanostores-computed-dynamic

size-limit:

	Atom: + 26 B
	All: + 159 B
btakita added a commit to btakita/nanostores that referenced this issue Jul 19, 2023
+ BoxTask,task,Task,UnboxTask

atom: is a function which takes an optional autosubscribe function & returns .get()

computed:

	+ task:
		(): returns computed store's value
		(store: AnyStore): computed store autosubscribes to the given store

package.json: description: + SolidJS

Thanks to @dtinth for autosubscribe function type inference which he implemented in https://github.com/dtinth/nanostores-computed-dynamic

size-limit:

	Atom: + 26 B
	All: + 159 B
btakita added a commit to btakita/nanostores that referenced this issue Jul 19, 2023
	BREAKING CHANGE: If the `$computed()` returns a `PromiseLike`, the `PromiseLike`'s `.then()` is called to set `$computed.value`.
    Mitigation: the `$computed()` can return an object wrapping the `PromiseLike`.
		During the synchronous run of the `computed` callback, the `task` is pushed onto the `taskStack`.

Task:

	+ save(newValue: Value): Task<Value>
	+ set(intermediateValue: Value): Task<Value>
	+ stale(): boolean
	+ undo(): Task<Value>

size-limit:

	All: + 78 B
btakita added a commit to btakita/nanostores that referenced this issue Jul 19, 2023
+ BoxTask,task,Task,UnboxTask

atom: is a function which takes an optional autosubscribe function & returns .get()

computed:

	+ task:
		(): returns computed store's value
		(store: AnyStore): computed store autosubscribes to the given store

package.json: description: + SolidJS

Thanks to @dtinth for autosubscribe function type inference which he implemented in https://github.com/dtinth/nanostores-computed-dynamic

size-limit:

	Atom: + 27 B
	All: + 157 B
btakita added a commit to btakita/nanostores that referenced this issue Jul 19, 2023
+ BoxTask,task,Task,UnboxTask

atom: is a function which takes an optional autosubscribe function & returns .get()

computed:

	+ task:
		(): returns computed store's value
		(store: AnyStore): computed store autosubscribes to the given store

package.json: description: + SolidJS

Thanks to @dtinth for autosubscribe function type inference which he implemented in https://github.com/dtinth/nanostores-computed-dynamic

size-limit:

	Atom: + 26 B
	All: + 144 B
btakita added a commit to btakita/nanostores that referenced this issue Jul 19, 2023
+ BoxTask,task,Task,UnboxTask

atom: is a function which takes an optional autosubscribe function & returns .get()

computed:

	+ task:
		(): returns computed store's value
		(store: AnyStore): computed store autosubscribes to the given store

package.json: description: + SolidJS

Thanks to @dtinth for autosubscribe function type inference which he implemented in https://github.com/dtinth/nanostores-computed-dynamic

size-limit:

	Atom: + 26 B
	All: + 137 B
btakita added a commit to btakita/nanostores that referenced this issue Jul 19, 2023
+ BoxTask,task,Task,UnboxTask

atom: is a function which takes an optional autosubscribe function & returns .get()

computed:

	+ task:
		(): returns computed store's value
		(store: AnyStore): computed store autosubscribes to the given store

package.json: description: + SolidJS

Thanks to @dtinth for autosubscribe function type inference which he implemented in https://github.com/dtinth/nanostores-computed-dynamic

size-limit:

	Atom: + 25 B
	All: + 136 B
btakita added a commit to btakita/nanostores that referenced this issue Jul 19, 2023
+ BoxTask,task,Task,UnboxTask

atom: is a function which takes an optional autosubscribe function & returns .get()

computed:

	+ task:
		(): returns computed store's value
		(store: AnyStore): computed store autosubscribes to the given store

package.json: description: + SolidJS

Thanks to @dtinth for autosubscribe function type inference which he implemented in https://github.com/dtinth/nanostores-computed-dynamic

size-limit:

	Atom: + 25 B
	All: + 132 B
btakita added a commit to btakita/nanostores that referenced this issue Jul 19, 2023
+ BoxTask,task,Task,UnboxTask

atom: is a function which takes an optional autosubscribe function & returns .get()

computed:

	+ task:
		(): returns computed store's value
		(store: AnyStore): computed store autosubscribes to the given store

package.json: description: + SolidJS

Thanks to @dtinth for autosubscribe function type inference which he implemented in https://github.com/dtinth/nanostores-computed-dynamic

size-limit:

	Atom: + 25 B
	All: + 132 B
btakita added a commit to btakita/nanostores that referenced this issue Jul 19, 2023
+ BoxTask,task,Task,UnboxTask

atom: is a function which takes an optional autosubscribe function & returns .get()

computed:

	+ task:
		(): returns computed store's value
		(store: AnyStore): computed store autosubscribes to the given store

package.json: description: + SolidJS

Thanks to @dtinth for autosubscribe function type inference which he implemented in https://github.com/dtinth/nanostores-computed-dynamic

size-limit:

	Atom: + 25 B
	All: + 132 B
btakita added a commit to btakita/nanostores that referenced this issue Jul 19, 2023
+ BoxTask,task,Task,UnboxTask

atom: is a function which takes an optional autosubscribe function & returns .get()

computed:

	+ task:
		(): returns computed store's value
		(store: AnyStore): computed store autosubscribes to the given store

package.json: description: + SolidJS

Thanks to @dtinth for autosubscribe function type inference which he implemented in https://github.com/dtinth/nanostores-computed-dynamic

size-limit:

	Atom: + 25 B
	All: + 131 B
btakita added a commit to btakita/nanostores that referenced this issue Jul 19, 2023
+ BoxTask,task,Task,UnboxTask

atom: is a function which takes an optional autosubscribe function & returns .get()

computed:

	+ task:
		(): returns computed store's value
		(store: AnyStore): computed store autosubscribes to the given store

package.json: description: + SolidJS

Thanks to @dtinth for autosubscribe function type inference which he implemented in https://github.com/dtinth/nanostores-computed-dynamic

size-limit:

	Atom: + 25 B
	All: + 121 B
btakita added a commit to btakita/nanostores that referenced this issue Jul 19, 2023
+ BoxTask,task,Task,UnboxTask

atom: is a function which takes an optional autosubscribe function & returns .get()

computed:

	+ task:
		(): returns computed store's value
		(store: AnyStore): computed store autosubscribes to the given store

package.json: description: + SolidJS

Thanks to @dtinth for autosubscribe function type inference which he implemented in https://github.com/dtinth/nanostores-computed-dynamic

size-limit:

	Atom: + 25 B
	All: + 115 B
btakita added a commit to btakita/nanostores that referenced this issue Jul 19, 2023
+ BoxTask,task,Task,UnboxTask

atom: is a function which takes an optional autosubscribe function & returns .get()

computed:

	+ task:
		(): returns computed store's value
		(store: AnyStore): computed store autosubscribes to the given store

package.json: description: + SolidJS

Thanks to @dtinth for autosubscribe function type inference which he implemented in https://github.com/dtinth/nanostores-computed-dynamic

size-limit:

	Atom: + 25 B
	All: + 115 B
btakita added a commit to btakita/nanostores that referenced this issue Jul 19, 2023
+ BoxTask,task,Task,UnboxTask

atom: is a function which takes an optional autosubscribe function & returns .get()

computed:

	+ task:
		(): returns computed store's value
		(store: AnyStore): computed store autosubscribes to the given store

package.json: description: + SolidJS

Thanks to @dtinth for autosubscribe function type inference which he implemented in https://github.com/dtinth/nanostores-computed-dynamic

size-limit:

	Atom: + 25 B
	All: + 115 B
btakita added a commit to btakita/nanostores that referenced this issue Jul 19, 2023
+ BoxTask,task,Task,UnboxTask

atom: is a function which takes an optional autosubscribe function & returns .get()

computed:

	+ task:
		(): returns computed store's value
		(store: AnyStore): computed store autosubscribes to the given store

package.json: description: + SolidJS

Thanks to @dtinth for autosubscribe function type inference which he implemented in https://github.com/dtinth/nanostores-computed-dynamic

size-limit:

	Atom: + 25 B
	All: + 109 B
btakita added a commit to btakita/nanostores that referenced this issue Jul 19, 2023
+ BoxTask,task,Task,UnboxTask

atom: is a function which takes an optional autosubscribe function & returns .get()

computed:

	+ task:
		(): returns computed store's value
		(store: AnyStore): computed store autosubscribes to the given store

package.json: description: + SolidJS

Thanks to @dtinth for autosubscribe function type inference which he implemented in https://github.com/dtinth/nanostores-computed-dynamic

size-limit:

	Atom: + 25 B
	All: + 109 B
btakita added a commit to btakita/nanostores that referenced this issue Jul 19, 2023
+ BoxTask,task,Task,UnboxTask

atom: is a function which takes an optional autosubscribe function & returns .get()

computed:

	+ task:
		(): returns computed store's value
		(store: AnyStore): computed store autosubscribes to the given store

package.json: description: + SolidJS

Thanks to @dtinth for autosubscribe function type inference which he implemented in https://github.com/dtinth/nanostores-computed-dynamic

size-limit:

	Atom: + 25 B
	All: + 109 B
btakita added a commit to btakita/nanostores that referenced this issue Jul 19, 2023
+ BoxTask,task,Task,UnboxTask

atom: is a function which takes an optional autosubscribe function & returns .get()

computed:

	+ task:
		(): returns computed store's value
		(store: AnyStore): computed store autosubscribes to the given store

package.json: description: + SolidJS

Thanks to @dtinth for autosubscribe function type inference which he implemented in https://github.com/dtinth/nanostores-computed-dynamic

size-limit:

	Atom: + 25 B
	All: + 109 B
btakita added a commit to btakita/nanostores that referenced this issue Jul 19, 2023
+ BoxTask,task,Task,UnboxTask

atom: is a function which takes an optional autosubscribe function & returns .get()

computed:

	+ task:
		(): returns computed store's value
		(store: AnyStore): computed store autosubscribes to the given store

package.json: description: + SolidJS

Thanks to @dtinth for autosubscribe function type inference which he implemented in https://github.com/dtinth/nanostores-computed-dynamic

size-limit:

	Atom: + 25 B
	All: + 106 B
btakita added a commit to btakita/nanostores that referenced this issue Jul 20, 2023
+ BoxTask,task,Task,UnboxTask

atom: is a function which takes an optional autosubscribe function & returns .get()

computed:

	+ task:
		(): returns computed store's value
		(store: AnyStore): computed store autosubscribes to the given store

package.json: description: + SolidJS

Thanks to @dtinth for autosubscribe function type inference which he implemented in https://github.com/dtinth/nanostores-computed-dynamic

size-limit:

	Atom: + 25 B
	All: + 106 B
btakita added a commit to btakita/nanostores that referenced this issue Jul 20, 2023
+ BoxTask,task,Task,UnboxTask

atom: is a function which takes an optional autosubscribe function & returns .get()

computed:

	+ task:
		(): returns computed store's value
		(store: AnyStore): computed store autosubscribes to the given store

package.json: description: + SolidJS

Thanks to @dtinth for autosubscribe function type inference which he implemented in https://github.com/dtinth/nanostores-computed-dynamic

size-limit:

	Atom: + 25 B
	All: + 106 B
btakita added a commit to btakita/nanostores that referenced this issue Jul 20, 2023
+ BoxTask,task,Task,UnboxTask

atom: is a function which takes an optional autosubscribe function & returns .get()

computed:

	+ task:
		(): returns computed store's value
		(store: AnyStore): computed store autosubscribes to the given store

package.json: description: + SolidJS

Thanks to @dtinth for autosubscribe function type inference which he implemented in https://github.com/dtinth/nanostores-computed-dynamic

size-limit:

	Atom: + 25 B
	All: + 106 B
btakita added a commit to btakita/nanostores that referenced this issue Sep 26, 2023
…tosubscribe (nanostores#188)

atom:

	+ .a: active autosubscribe when atom was instantiated

computed:

	+ autosubscribe:
		(): returns computed store's value
		(store: AnyStore): computed store autosubscribes to the given store
		(cb: () => any): runs the cb & autosubscribes to any called store functions within the cb
		.off(cb): hook which runs callback onUnmount & before the computed cb being called
		.on(cb): hook which runs callback onMount
		.save(): sets computed & sets undoValue
		.set(newValue): sets computed & does not set undoValue
		.stale(): returns true if computed store's cb was called again after call associated with the autosubscribe
		.undo: sets computed to undoValue
		a stale autosubscribe has some of it's methods neutered:
			.off
			.on
			.save
			.set
			.undo
	+ async support

package.json: description: + SolidJS

Thanks to @dtinth for autosubscribe function type inference which he implemented in https://github.com/dtinth/nanostores-computed-dynamic

size-limit:

	Atom: + 15 B
	All: + 300 B
btakita added a commit to btakita/nanostores that referenced this issue Sep 26, 2023
…tosubscribe (nanostores#188)

atom:

	+ .a: active autosubscribe when atom was instantiated

computed:

	+ autosubscribe:
		(): returns computed store's value
		(store: AnyStore): computed store autosubscribes to the given store
		(cb: () => any): runs the cb & autosubscribes to any called store functions within the cb
		.off(cb): hook which runs callback onUnmount & before the computed cb being called
		.on(cb): hook which runs callback onMount
		.save(): sets computed & sets undoValue
		.set(newValue): sets computed & does not set undoValue
		.stale(): returns true if computed store's cb was called again after call associated with the autosubscribe
		.undo: sets computed to undoValue
		a stale autosubscribe has some of it's methods neutered:
			.off
			.on
			.save
			.set
			.undo
	+ async support

package.json: description: + SolidJS

Thanks to @dtinth for autosubscribe function type inference which he implemented in https://github.com/dtinth/nanostores-computed-dynamic

size-limit:

	Atom: + 12 B
	All: + 297 B
btakita added a commit to btakita/nanostores that referenced this issue Sep 26, 2023
…nostores#188)

atom: is a function which takes an optional autosubscribe function & returns .get()

	+ .a: active autosubscribe when atom was instantiated

computed:

	+ autosubscribe:
		(): returns computed store's value
		(store: AnyStore): computed store autosubscribes to the given store
		(cb: () => any): runs the cb & autosubscribes to any called store functions within the cb
		.off(cb): hook which runs callback onUnmount & before the computed cb being called
		.on(cb): hook which runs callback onMount
		.save(): sets computed & sets undoValue
		.set(newValue): sets computed & does not set undoValue
		.stale(): returns true if computed store's cb was called again after call associated with the autosubscribe
		.undo: sets computed to undoValue
		a stale autosubscribe has some of it's methods neutered:
			.onStart
			.onStop
			.save
			.set
			.undo
	+ async support

package.json: description: + SolidJS

Thanks to @dtinth for autosubscribe function type inference which he implemented in https://github.com/dtinth/nanostores-computed-dynamic

size-limit:

	Atom: + 25 B
	All: + 297 B
btakita added a commit to btakita/nanostores that referenced this issue Oct 21, 2023
…nostores#188)

atom: is a function which takes an optional autosubscribe function & returns .get()

	+ .a: active autosubscribe when atom was instantiated

computed:

	+ autosubscribe:
		(): returns computed store's value
		(store: AnyStore): computed store autosubscribes to the given store
		(cb: () => any): runs the cb & autosubscribes to any called store functions within the cb
		.off(cb): hook which runs callback onUnmount & before the computed cb being called
		.on(cb): hook which runs callback onMount
		.save(): sets computed & sets undoValue
		.set(newValue): sets computed & does not set undoValue
		.stale(): returns true if computed store's cb was called again after call associated with the autosubscribe
		.undo: sets computed to undoValue
		a stale autosubscribe has some of it's methods neutered:
			.onStart
			.onStop
			.save
			.set
			.undo
	+ async support

package.json: description: + SolidJS

Thanks to @dtinth for autosubscribe function type inference which he implemented in https://github.com/dtinth/nanostores-computed-dynamic

size-limit:

	Atom: + 25 B
	All: + 297 B
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants