Skip to content

Feature/plx-1-functions-async-sync-as-an-allowed-init #43

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

Open
wants to merge 1 commit into
base: canary
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/docs/api-reference/CollectionInstance.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,8 @@ A Collection Instance

<p>
Get the Value of the data item with the provided key (the raw data). If there
is not an existing data item, this will return a <em>provisional</em> one
is not an existing data item, this will return a <em>provisional</em> one (an
empty untracked data instance)
</p>

**Returns**: <code>this</code> <p>The new Collection Instance</p>
Expand Down
5 changes: 3 additions & 2 deletions packages/plexus-core/src/collection/collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ export class CollectionInstance<
return this
}
/**
* Get the Value of the data item with the provided key (the raw data). If there is not an existing data item, this will return a _provisional_ one
* Get the Value of the data item with the provided key (the raw data). If there is not an existing data item, this will return a _provisional_ one (an empty untracked data instance)
* @param {string|number} dataKey The key of the data item to get
* @returns {this} The new Collection Instance
*/
Expand Down Expand Up @@ -469,7 +469,8 @@ export class CollectionInstance<
*/
createGroup<Name extends GroupName>(
groupName: Name,
config?: PlexusCollectionGroupConfig<DataType>
config?: PlexusCollectionGroupConfig<DataType>,
fetcher?: (currentValue?: DataType) => DataType
) {
this.mount()
if (this._internalStore._groups.has(groupName)) return this
Expand Down
11 changes: 7 additions & 4 deletions packages/plexus-core/src/collection/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ export class CollectionData<

// loop through the foreign keys
for (idKey of Object.keys(foreignKeys ?? {})) {
const isArray = foreignKeys[idKey]?.mode === 'array';
const isArray = foreignKeys[idKey]?.mode === 'array'
const newKey = foreignKeys[idKey]?.newKey as string
const foreignCollectionName = foreignKeys[idKey]?.reference as string
const foreignCollection = this.instance().findReference(
Expand All @@ -132,9 +132,12 @@ export class CollectionData<

// if we have a shallow value, then we can try to get the fresh value from the foreign collection
if (this.shallowValue) {
const freshValue = isArray ?
this.shallowValue?.[idKey]?.map((id: string) => foreignCollection?.getItem(id).shallowValue) || undefined
: foreignCollection?.getItem(this.shallowValue?.[idKey]).shallowValue || undefined
const freshValue = isArray
? this.shallowValue?.[idKey]?.map(
(id: string) => foreignCollection?.getItem(id).shallowValue
) || undefined
: foreignCollection?.getItem(this.shallowValue?.[idKey])
.shallowValue || undefined
if (
freshValue &&
foreignCollection?.config.foreignKeys?.[idKey]?.newKey
Expand Down
4 changes: 2 additions & 2 deletions packages/plexus-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ import { LiteralType, AlmostAnything } from '@plexusjs/utils'
export function state<
Literal extends PlexusStateType = any,
Value = Literal extends AlmostAnything ? Literal : LiteralType<Literal>
>(item: Value) {
return _state<Value>(() => instance(), item)
>(item: Value, fetcher?: (currentValue?: Value) => Value) {
return _state<Value>(() => instance(), item, fetcher)
}
/**
* Generate a Plexus State
Expand Down
8 changes: 8 additions & 0 deletions packages/plexus-core/src/initializer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Watchable } from '.'

export function WatchableInitializer<ValueType extends any>(
watchable: Watchable<ValueType>,
initFunction: (currentValue?: ValueType) => ValueType
) {
watchable
}
9 changes: 5 additions & 4 deletions packages/plexus-core/src/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ export class StateInstance<
constructor(
instance: () => PlexusInstance,
init: StateValue,
fetcher?: (currentValue: StateValue) => StateValue
fetcher?: (currentValue?: StateValue) => StateValue
) {
super(instance, init)
super(instance, init, fetcher)
this.instance = instance
this._internalStore = {
_name: '',
Expand Down Expand Up @@ -267,9 +267,10 @@ export class StateInstance<

export function _state<StateValue extends PlexusStateType>(
instance: () => PlexusInstance,
_init: StateValue
_init: StateValue,
fetcher?: (currentValue?: StateValue) => StateValue
) {
// Returned Object //

return new StateInstance<StateValue>(instance, _init)
return new StateInstance<StateValue>(instance, _init, fetcher)
}
37 changes: 34 additions & 3 deletions packages/plexus-core/src/watchable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ interface WatchableStore<Value = any> {
_nextValue: Value
_watchers: Set<PlexusWatcher<Value>>
_internalId: string
_initialized: boolean
}

type HistorySeed<ValueType = any> = {
Expand All @@ -22,15 +23,26 @@ type HistorySeed<ValueType = any> = {
export class Watchable<ValueType = any> {
protected _watchableStore: WatchableStore<ValueType>
protected instance: () => PlexusInstance
protected initFetcher: ((currentValue?: ValueType) => ValueType) | undefined
private loading: boolean = false
public get isLoading() {
return this.loading
}

/**
* The internal id of the computed state
*/
get id(): string {
return `${this._watchableStore._internalId}`
}
constructor(instance: () => PlexusInstance, init: ValueType) {
constructor(
instance: () => PlexusInstance,
init: ValueType,
fetcher?: (currentValue?: ValueType) => ValueType
) {
this.instance = instance
this._watchableStore = {
_initialized: false,
_internalId: instance().genId(),
_nextValue: init,
_value: init,
Expand All @@ -39,6 +51,14 @@ export class Watchable<ValueType = any> {
_lastValue: null,
_watchers: new Set(),
}
this.initFetcher = fetcher
? (currentValue?: ValueType) => {
this.loading = true
const newValue = fetcher?.(currentValue)
this.loading = false
return newValue || init
}
: undefined
}

watch<Value extends ValueType = ValueType>(
Expand All @@ -54,6 +74,13 @@ export class Watchable<ValueType = any> {
}
}
get value(): ValueType {
if (this._watchableStore._initialized === false && this.initFetcher) {
this._watchableStore._value =
this.initFetcher?.(this._watchableStore._value) ||
this._watchableStore._value
this._watchableStore._publicValue = deepClone(this._watchableStore._value)
this._watchableStore._initialized = true
}
return this._watchableStore._publicValue
}
}
Expand All @@ -62,8 +89,12 @@ export class WatchableMutable<
ValueType extends NonNullable<any> = any
> extends Watchable<ValueType> {
private _history: HistorySeed | undefined
constructor(instance: () => PlexusInstance, init: ValueType) {
super(instance, init)
constructor(
instance: () => PlexusInstance,
init: ValueType,
fetcher?: (currentValue?: ValueType) => ValueType
) {
super(instance, init, fetcher)
}

/**
Expand Down
2 changes: 1 addition & 1 deletion packages/plexus-react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ export { useEvent } from './useEvent'

export { useDeposit } from './useDeposit'

export { useInit } from './useInit'
export { useInit } from './useInit'
46 changes: 26 additions & 20 deletions packages/plexus-react/src/useInit.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,38 @@
import { PlexusAction, Watchable } from '@plexusjs/core';
import { FunctionType, InnerFunction } from '@plexusjs/core/dist/action';
import { useEffect, useState } from 'react';
import { usePlexus, PlexusValue, PlexusValueArray } from './usePlexus';
import { PlexusAction, Watchable } from '@plexusjs/core'
import { FunctionType, InnerFunction } from '@plexusjs/core/dist/action'
import { useEffect, useState } from 'react'
import { usePlexus, PlexusValue, PlexusValueArray } from './usePlexus'

export type PlexusInitReturn<T> = {
value: T;
loading: boolean;
value: T
loading: boolean
}

// Singleton argument
export function useInit<V extends Watchable, Fn extends FunctionType>(deps: V, action: InnerFunction<Fn>): PlexusInitReturn<PlexusValue<V>>
export function useInit<V extends Watchable, Fn extends FunctionType>(
deps: V,
action: InnerFunction<Fn>
): PlexusInitReturn<PlexusValue<V>>
// array argument
export function useInit<V extends Watchable[], Fn extends FunctionType>(
deps: V | [],
action: InnerFunction<Fn>
action: InnerFunction<Fn>
): PlexusInitReturn<PlexusValueArray<V>>

export function useInit <Fn extends FunctionType> (deps: typeof usePlexus.arguments[0], action: InnerFunction<Fn>) {
const value = usePlexus(deps);
const [loading, setLoading] = useState(true);
export function useInit<Fn extends FunctionType>(
deps: typeof usePlexus.arguments[0],
action: InnerFunction<Fn>
) {
const value = usePlexus(deps)
const [loading, setLoading] = useState(true)

useEffect(() => {
setLoading(true);
action().then(() => setLoading(false));
}, [action])
useEffect(() => {
setLoading(true)
action().then(() => setLoading(false))
}, [action])

return {
loading,
value
}
}
return {
loading,
value,
}
}
12 changes: 12 additions & 0 deletions packages/plexus-utils/src/shared/itemManipulation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,3 +147,15 @@ export function isEqual(a: NonNullable<any>, b: NonNullable<any>): boolean {
}
return false
}

/**
*
*/
// export function asyncNormalizer<
// InputFunction extends () => Promise<unknown> | unknown
// >(
// inputFunction: InputFunction
// ): [ReturnType<InputFunction> | undefined, Error | undefined] {

// // return [inputFunction()]
// }