Skip to content

Commit

Permalink
refactor(hooks): refactor useSignal, useComputed
Browse files Browse the repository at this point in the history
  • Loading branch information
honghuangdc committed Apr 25, 2024
1 parent dcd51f4 commit 3b5e4b3
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 82 deletions.
2 changes: 1 addition & 1 deletion packages/hooks/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ import useHookTable from './use-table';

export { useBoolean, useLoading, useCountDown, useContext, useSvgIconRender, useHookTable };

export * from './use-state';
export * from './use-signal';
export * from './use-table';
105 changes: 105 additions & 0 deletions packages/hooks/src/use-signal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { computed, shallowRef, triggerRef } from 'vue';
import type { ComputedGetter, DebuggerOptions, ShallowRef, WritableComputedOptions, WritableComputedRef } from 'vue';

type Updater<T> = (value: T) => T;
type Mutator<T> = (value: T) => void;

/**
* Signal is a reactive value that can be set, updated or mutated
*
* ```ts
* const count = useSignal(0);
*
* // `watchEffect`
* watchEffect(() => {
* console.log(count());
* });
*
* // watch
* watch(count, value => {
* console.log(value);
* });
*
* // useComputed
* const double = useComputed(() => count() * 2);
* const writeableDouble = useComputed({
* get: () => count() * 2,
* set: value => count.set(value / 2)
* });
* ```
*/
export interface Signal<T> {
(): Readonly<T>;
/**
* Set the value of the signal
*
* It recommend use `set` for primitive values
*
* @param value
*/
set(value: T): void;
/**
* Update the value of the signal using an updater function
*
* It recommend use `update` for non-primitive values, only the first level of the object will be reactive.
*
* @param updater
*/
update(updater: Updater<T>): void;
/**
* Mutate the value of the signal using a mutator function
*
* this action will call `triggerRef`, so the value will be tracked on `watchEffect`.
*
* It recommend use `mutate` for non-primitive values, all levels of the object will be reactive.
*
* @param mutator
*/
mutate(mutator: Mutator<T>): void;
}

export interface ReadonlySignal<T> {
(): Readonly<T>;
}

export function useSignal<T>(initialValue: T): Signal<T> {
const state = shallowRef(initialValue);

return createSignal(state);
}

export function useComputed<T>(getter: ComputedGetter<T>, debugOptions?: DebuggerOptions): ReadonlySignal<T>;
export function useComputed<T>(options: WritableComputedOptions<T>, debugOptions?: DebuggerOptions): Signal<T>;
export function useComputed<T>(
getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>,
debugOptions?: DebuggerOptions
) {
const isGetter = typeof getterOrOptions === 'function';

const computedValue = computed(getterOrOptions as any, debugOptions);

if (isGetter) {
return () => computedValue.value as ReadonlySignal<T>;
}

return createSignal(computedValue);
}

function createSignal<T>(state: ShallowRef<T> | WritableComputedRef<T>): Signal<T> {
const signal = () => state.value;

signal.set = (value: T) => {
state.value = value;
};

signal.update = (updater: Updater<T>) => {
state.value = updater(state.value);
};

signal.mutate = (mutator: Mutator<T>) => {
mutator(state.value);
triggerRef(state);
};

return signal;
}
81 changes: 0 additions & 81 deletions packages/hooks/src/use-state.ts

This file was deleted.

0 comments on commit 3b5e4b3

Please sign in to comment.