refactor(hooks): refactor useSignal, useComputed
This commit is contained in:
parent
dcd51f4cda
commit
3b5e4b3405
@ -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
packages/hooks/src/use-signal.ts
Normal file
105
packages/hooks/src/use-signal.ts
Normal file
@ -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;
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
import { ref } from 'vue';
|
||||
import type { Ref } from 'vue';
|
||||
|
||||
/**
|
||||
* useRef
|
||||
*
|
||||
* it is a simple ref management hook wrapped by vue3's ref function.
|
||||
*
|
||||
* to resolve the ref type problem about `UnwrapRef`
|
||||
*
|
||||
* @param initValue
|
||||
*/
|
||||
export function useRef<T>(initValue: T) {
|
||||
const refValue = ref(initValue) as Ref<T>;
|
||||
|
||||
return refValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* useState
|
||||
*
|
||||
* define a state and a setState function
|
||||
*
|
||||
* @param initValue
|
||||
*/
|
||||
export function useState<T>(initValue: T) {
|
||||
const state = useRef(initValue);
|
||||
|
||||
function setState(value: T) {
|
||||
state.value = value;
|
||||
}
|
||||
|
||||
return [state, setState] as const;
|
||||
}
|
||||
|
||||
interface Signal<T> {
|
||||
(): T;
|
||||
/**
|
||||
* the ref object of the signal, but it is readonly
|
||||
*
|
||||
* equal to `const ref = ref(initValue);`
|
||||
*/
|
||||
readonly ref: Readonly<Ref<T>>;
|
||||
/**
|
||||
* set the value of the signal
|
||||
*
|
||||
* @param value
|
||||
*/
|
||||
set(value: T): void;
|
||||
/**
|
||||
* update the value of the signal
|
||||
*
|
||||
* @param fn update function
|
||||
*/
|
||||
update(fn: (value: T) => T): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* useSignal
|
||||
*
|
||||
* @param initValue
|
||||
*/
|
||||
export function useSignal<T>(initValue: T) {
|
||||
const [state, setState] = useState(initValue);
|
||||
|
||||
function updateState(fn: (value: T) => T) {
|
||||
const updatedValue = fn(state.value);
|
||||
setState(updatedValue);
|
||||
}
|
||||
|
||||
const signal = function signal() {
|
||||
return state.value;
|
||||
} as Signal<T>;
|
||||
|
||||
(signal as any).ref = state;
|
||||
|
||||
signal.set = setState;
|
||||
signal.update = updateState;
|
||||
|
||||
return signal;
|
||||
}
|
Loading…
Reference in New Issue
Block a user