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

feat/correct state type #48

Merged
merged 5 commits into from
Jul 26, 2024
Merged
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
6 changes: 6 additions & 0 deletions .changeset/great-wasps-juggle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@pionjs/pion": minor
---

Improve the type definition of useState and useProperty adding better support for initial values
and return better defined state updaters.
2 changes: 2 additions & 0 deletions src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,5 @@ export { State } from "./state";

export type { Ref } from "./use-ref";
export type { Options as ComponentOptions } from "./component";

export type { StateUpdater } from "./use-state";
31 changes: 17 additions & 14 deletions src/use-property.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,35 @@
import { hook, Hook } from "./hook";
import { State } from "./state";
import type {
InitialState,
NewState,
StateUpdater,
StateTuple,
} from "./use-state";

type Host<T> = Element & { [key: string]: T };
type NewState<T> = T | ((previousState?: T) => T);
type StateUpdater<T> = (value: NewState<T>) => void;
type ChangeEvent<T> = {
value: T;
path: string;
};

export interface UseProperty {
<T>(property: string): StateTuple<T | undefined>;
<T>(property: string, value?: InitialState<T>): StateTuple<T>;
}

const UPPER = /([A-Z])/gu;

export const useProperty = hook(
class<T> extends Hook<[string, T], [T, StateUpdater<T>], Host<T>> {
class<T> extends Hook<[string, T], StateTuple<T>, Host<T>> {
property: string;
eventName: string;

constructor(
id: number,
state: State<Host<T>>,
property: string,
initialValue: NewState<T>
initialValue: InitialState<T>
) {
super(id, state);

Expand All @@ -46,15 +55,15 @@ export const useProperty = hook(
this.updateProp(initialValue);
}

update(ignored: string, ignored2: T): [T, StateUpdater<T>] {
update(ignored: string, ignored2: T): StateTuple<T> {
return [this.state.host[this.property], this.updater];
}

updater(value: NewState<T>): void {
const previousValue = this.state.host[this.property];

if (typeof value === "function") {
const updaterFn = value as (previousState?: T) => T;
const updaterFn = value as (previousState: T) => T;
value = updaterFn(previousValue);
}

Expand All @@ -80,16 +89,10 @@ export const useProperty = hook(
return ev;
}
}
) as <T>(
property: string,
initialValue?: T
) => readonly [
T extends (...args: any[]) => infer R ? R : T,
StateUpdater<T extends (...args: any[]) => infer S ? S : T>
];
) as UseProperty;

export const lift =
<T>(setter: (value: T) => void) =>
<T>(setter: StateUpdater<T>) =>
(ev: CustomEvent<ChangeEvent<T>>) => {
ev.preventDefault();
setter(ev.detail.value);
Expand Down
33 changes: 18 additions & 15 deletions src/use-state.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,46 @@
import { hook, Hook } from "./hook";
import { State } from "./state";

type NewState<T> = T | ((previousState?: T) => T);
type StateUpdater<T> = (value: NewState<T>) => void;
export type InitialState<T> = T | (() => T);
export type NewState<T> = T | ((previousState: T) => T);
export type StateUpdater<T> = (value: NewState<T>) => void;
export type StateTuple<T> = readonly [T, StateUpdater<T>];

export interface UseState {
<T>(): StateTuple<T | undefined>;
<T>(value?: InitialState<T>): StateTuple<T>;
}

/**
* @function
* @template {*} T
* @param {T} [initialState] - Optional initial state
* @return {readonly [state: T, updaterFn: StateUpdater<T>]} stateTuple - Tuple of current state and state updater function
* @return {StateTuple<T>} stateTuple - Tuple of current state and state updater function
*/
const useState = hook(
class<T> extends Hook {
args!: readonly [T, StateUpdater<T>];
args!: StateTuple<T>;

constructor(id: number, state: State, initialValue: T) {
constructor(id: number, state: State, initialValue: InitialState<T>) {
super(id, state);
this.updater = this.updater.bind(this);

if (typeof initialValue === "function") {
initialValue = initialValue();
const initFn = initialValue as () => T;
initialValue = initFn();
}

this.makeArgs(initialValue);
}

update(): readonly [T, StateUpdater<T>] {
update(): StateTuple<T> {
return this.args;
}

updater(value: NewState<T>): void {
const [previousValue] = this.args;
if (typeof value === "function") {
const updaterFn = value as (previousState?: T) => T;
const updaterFn = value as (previousState: T) => T;
value = updaterFn(previousValue);
}

Expand All @@ -45,14 +53,9 @@ const useState = hook(
}

makeArgs(value: T): void {
this.args = Object.freeze([value, this.updater] as const);
this.args = Object.freeze([value, this.updater]);
}
}
) as <T>(
initialValue?: T
) => readonly [
T extends (...args: any[]) => infer R ? R : T,
StateUpdater<T extends (...args: any[]) => infer S ? S : T>
];
) as UseState;

export { useState };
Loading