Skip to content

Commit 8ff7db9

Browse files
committed
Improve the defintion of useState, better handling initial value.
1 parent 4705ede commit 8ff7db9

File tree

3 files changed

+24
-15
lines changed

3 files changed

+24
-15
lines changed

.changeset/salty-teams-nail.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"haunted": minor
3+
---
4+
5+
Improve the defintion of useState, better handling initial value.

src/core.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,4 @@ export { BaseScheduler } from "./scheduler";
5353
export { State } from "./state";
5454
export type { Ref } from "./use-ref";
5555
export type { Options as ComponentOptions } from "./component";
56+
export type { StateUpdater } from "./use-state";

src/use-state.ts

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,46 @@
11
import { hook, Hook } from "./hook";
22
import { State } from "./state";
33

4-
type NewState<T> = T | ((previousState?: T) => T);
5-
type StateUpdater<T> = (value: NewState<T>) => void;
4+
export type InitialState<T> = T | (() => T);
5+
export type NewState<T> = T | ((previousState: T) => T);
6+
export type StateUpdater<T> = (value: NewState<T>) => void;
7+
export type StateTuple<T> = readonly [T, StateUpdater<T>];
8+
9+
export interface UseState {
10+
<T>(): StateTuple<T | undefined>;
11+
<T>(value?: InitialState<T>): StateTuple<T>;
12+
}
613

714
/**
815
* @function
916
* @template {*} T
1017
* @param {T} [initialState] - Optional initial state
11-
* @return {readonly [state: T, updaterFn: StateUpdater<T>]} stateTuple - Tuple of current state and state updater function
18+
* @return {StateTuple<T>} stateTuple - Tuple of current state and state updater function
1219
*/
1320
const useState = hook(
1421
class<T> extends Hook {
15-
args!: readonly [T, StateUpdater<T>];
22+
args!: StateTuple<T>;
1623

17-
constructor(id: number, state: State, initialValue: T) {
24+
constructor(id: number, state: State, initialValue: InitialState<T>) {
1825
super(id, state);
1926
this.updater = this.updater.bind(this);
2027

2128
if (typeof initialValue === "function") {
22-
initialValue = initialValue();
29+
const initFn = initialValue as () => T;
30+
initialValue = initFn();
2331
}
2432

2533
this.makeArgs(initialValue);
2634
}
2735

28-
update(): readonly [T, StateUpdater<T>] {
36+
update(): StateTuple<T> {
2937
return this.args;
3038
}
3139

3240
updater(value: NewState<T>): void {
3341
const [previousValue] = this.args;
3442
if (typeof value === "function") {
35-
const updaterFn = value as (previousState?: T) => T;
43+
const updaterFn = value as (previousState: T) => T;
3644
value = updaterFn(previousValue);
3745
}
3846

@@ -45,14 +53,9 @@ const useState = hook(
4553
}
4654

4755
makeArgs(value: T): void {
48-
this.args = Object.freeze([value, this.updater] as const);
56+
this.args = Object.freeze([value, this.updater]);
4957
}
5058
}
51-
) as <T>(
52-
initialValue?: T
53-
) => readonly [
54-
T extends (...args: any[]) => infer R ? R : T,
55-
StateUpdater<T extends (...args: any[]) => infer S ? S : T>
56-
];
59+
) as UseState;
5760

5861
export { useState };

0 commit comments

Comments
 (0)