mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
refactor(store): clean up
This commit is contained in:
@@ -32,7 +32,7 @@ interface Queue<Tasks> {
|
||||
abort(name?: keyof Tasks): void;
|
||||
reset(name?: keyof Tasks): void;
|
||||
destroy(): void;
|
||||
subscribe(listener: QueueListener<Tasks>): () => void;
|
||||
subscribe(callback: StateChange<TasksRecord<Tasks>>): () => void;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -147,7 +147,7 @@ const myPlugin = () => {
|
||||
interface Store<T> {
|
||||
get(): T;
|
||||
set(value: T | ((prev: T) => T)): void;
|
||||
subscribe(listener: (value: T) => void): () => void;
|
||||
subscribe(callback: (value: T) => void): () => void;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ The minimal interface any framework can consume:
|
||||
```ts
|
||||
interface Store<T> {
|
||||
get(): T;
|
||||
subscribe(listener: (value: T) => void): () => void;
|
||||
subscribe(callback: (value: T) => void): () => void;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -309,9 +309,9 @@ function isQueue(value: unknown): value is Queue {
|
||||
Subscriptions return an unsubscribe function:
|
||||
|
||||
```ts
|
||||
subscribe(listener: Listener): () => void {
|
||||
this.#subscribers.add(listener);
|
||||
return () => this.#subscribers.delete(listener);
|
||||
subscribe(callback: Callback): () => void {
|
||||
this.#subscribers.add(callback);
|
||||
return () => this.#subscribers.delete(callback);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
+5
-1
@@ -3,7 +3,11 @@
|
||||
"html": {
|
||||
"experimentalFullSupportEnabled": false
|
||||
},
|
||||
"assist": { "actions": { "source": { "organizeImports": "on" } } },
|
||||
"assist": {
|
||||
"actions": {
|
||||
"source": { "organizeImports": "on" }
|
||||
}
|
||||
},
|
||||
"formatter": {
|
||||
"enabled": true,
|
||||
"indentStyle": "space",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
import { createFeature } from '@videojs/store';
|
||||
import { createStore } from '@videojs/store/react';
|
||||
import { createStore, useStoreContext } from '@videojs/store/react';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { Video } from '../video';
|
||||
@@ -75,12 +75,12 @@ describe('video', () => {
|
||||
});
|
||||
|
||||
it('attaches video to store on mount', () => {
|
||||
const { Provider, useStore } = createTestStore();
|
||||
const { Provider } = createTestStore();
|
||||
|
||||
let attachCalled = false;
|
||||
|
||||
function TestComponent() {
|
||||
const store = useStore();
|
||||
const store = useStoreContext();
|
||||
|
||||
// Spy on attach
|
||||
const originalAttach = store.attach.bind(store);
|
||||
|
||||
@@ -62,9 +62,9 @@ export class Computed<T extends object, K extends keyof T, R> {
|
||||
return this.#cached;
|
||||
}
|
||||
|
||||
subscribe(listener: () => void): () => void {
|
||||
this.#listeners.add(listener);
|
||||
return () => this.#listeners.delete(listener);
|
||||
subscribe(callback: () => void): () => void {
|
||||
this.#listeners.add(callback);
|
||||
return () => this.#listeners.delete(callback);
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { abortable } from '@videojs/utils/events';
|
||||
import { isUndefined } from '@videojs/utils/predicate';
|
||||
import { StoreError } from './errors';
|
||||
import type { Request, RequestMeta, RequestMode } from './request';
|
||||
import type { WritableState } from './state';
|
||||
import type { StateChange, WritableState } from './state';
|
||||
import { createState } from './state';
|
||||
import type { ErrorTask, PendingTask, SuccessTask, Task, TaskContext, TaskKey } from './task';
|
||||
|
||||
@@ -47,8 +47,8 @@ export class Queue<Tasks extends TaskRecord = DefaultTaskRecord> {
|
||||
}
|
||||
|
||||
/** Subscribe to task changes. */
|
||||
subscribe(listener: (changedKeys: ReadonlySet<PropertyKey>) => void): () => void {
|
||||
return this.#tasks.subscribe(listener);
|
||||
subscribe(callback: StateChange<TasksRecord<Tasks>>): () => void {
|
||||
return this.#tasks.subscribe(callback);
|
||||
}
|
||||
|
||||
constructor() {
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
type Listener = (changedKeys: ReadonlySet<PropertyKey>) => void;
|
||||
export type StateChange<T, K extends keyof T = keyof T> = (changedKeys: ReadonlySet<K>) => void;
|
||||
|
||||
export interface State<T extends object> {
|
||||
readonly current: Readonly<T>;
|
||||
subscribe: ((listener: Listener) => () => void) & (<K extends keyof T>(keys: K[], listener: Listener) => () => void);
|
||||
subscribe<K extends keyof T>(keys: K[], callback: StateChange<T, K>): () => void;
|
||||
subscribe(callback: StateChange<T>): () => void;
|
||||
}
|
||||
|
||||
export interface WritableState<T extends object> extends State<T> {
|
||||
@@ -19,7 +20,7 @@ function scheduleFlush(): void {
|
||||
queueMicrotask(flush);
|
||||
}
|
||||
|
||||
const pendingContainers = new Set<StateContainer<any>>();
|
||||
const pendingContainers = new Set<StateContainer<any, any>>();
|
||||
|
||||
export function flush(): void {
|
||||
flushScheduled = false;
|
||||
@@ -33,11 +34,11 @@ export function flush(): void {
|
||||
|
||||
const hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
|
||||
class StateContainer<T extends object> implements WritableState<T> {
|
||||
class StateContainer<T extends object, K extends keyof T> implements WritableState<T> {
|
||||
#current: T;
|
||||
#listeners = new Set<Listener>();
|
||||
#keyListeners = new Map<PropertyKey, Set<Listener>>();
|
||||
#pending = new Set<PropertyKey>();
|
||||
#listeners = new Set<StateChange<T>>();
|
||||
#keyListeners = new Map<K, Set<StateChange<T>>>();
|
||||
#pending = new Set<K>();
|
||||
|
||||
constructor(initial: T) {
|
||||
this.#current = Object.freeze({ ...initial });
|
||||
@@ -50,7 +51,7 @@ class StateContainer<T extends object> implements WritableState<T> {
|
||||
set<K extends keyof T>(key: K, value: T[K]): void {
|
||||
if (Object.is(this.#current[key], value)) return;
|
||||
this.#current = Object.freeze({ ...this.#current, [key]: value });
|
||||
this.#pending.add(key);
|
||||
this.#pending.add(key as any);
|
||||
pendingContainers.add(this);
|
||||
scheduleFlush();
|
||||
}
|
||||
@@ -59,7 +60,7 @@ class StateContainer<T extends object> implements WritableState<T> {
|
||||
if (!(key in this.#current)) return;
|
||||
const { [key]: _, ...rest } = this.#current;
|
||||
this.#current = Object.freeze(rest as T);
|
||||
this.#pending.add(key);
|
||||
this.#pending.add(key as any);
|
||||
pendingContainers.add(this);
|
||||
scheduleFlush();
|
||||
}
|
||||
@@ -74,7 +75,7 @@ class StateContainer<T extends object> implements WritableState<T> {
|
||||
|
||||
if (!Object.is(this.#current[key], value)) {
|
||||
next[key] = value!;
|
||||
this.#pending.add(key);
|
||||
this.#pending.add(key as any);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,38 +86,39 @@ class StateContainer<T extends object> implements WritableState<T> {
|
||||
}
|
||||
}
|
||||
|
||||
subscribe(listener: Listener): () => void;
|
||||
subscribe<K extends keyof T>(keys: K[], listener: Listener): () => void;
|
||||
subscribe(first: Listener | PropertyKey[], second?: Listener): () => void {
|
||||
subscribe(callback: StateChange<T>): () => void;
|
||||
subscribe<K extends keyof T>(keys: K[], callback: StateChange<T, K>): () => void;
|
||||
subscribe(first: StateChange<T> | K[], second?: StateChange<T>): () => void {
|
||||
// Key-specific subscription
|
||||
if (Array.isArray(first)) {
|
||||
const keys = first;
|
||||
const listener = second!;
|
||||
const callback = second!;
|
||||
|
||||
for (const key of keys) {
|
||||
let set = this.#keyListeners.get(key);
|
||||
if (!set) this.#keyListeners.set(key, (set = new Set()));
|
||||
set.add(listener);
|
||||
set.add(callback);
|
||||
}
|
||||
|
||||
return () => {
|
||||
for (const key of keys) {
|
||||
this.#keyListeners.get(key)?.delete(listener);
|
||||
this.#keyListeners.get(key)?.delete(callback);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Global subscription
|
||||
const listener = first;
|
||||
this.#listeners.add(listener);
|
||||
const callback = first;
|
||||
this.#listeners.add(callback);
|
||||
|
||||
return () => this.#listeners.delete(listener);
|
||||
return () => this.#listeners.delete(callback);
|
||||
}
|
||||
|
||||
flush(): void {
|
||||
if (this.#pending.size === 0) return;
|
||||
|
||||
const keys: ReadonlySet<PropertyKey> = new Set(this.#pending);
|
||||
const keys: ReadonlySet<K> = new Set(this.#pending);
|
||||
|
||||
this.#pending.clear();
|
||||
|
||||
for (const fn of this.#listeners) fn(keys);
|
||||
|
||||
@@ -12,7 +12,7 @@ import type {
|
||||
import { Queue } from './queue';
|
||||
import type { RequestMeta, RequestMetaInit, ResolvedRequestConfig } from './request';
|
||||
import { CANCEL_ALL, createRequestMeta, resolveRequestCancel, resolveRequestKey } from './request';
|
||||
import type { WritableState } from './state';
|
||||
import type { StateChange, WritableState } from './state';
|
||||
import { createState } from './state';
|
||||
import type { PendingTask, Task, TaskContext } from './task';
|
||||
|
||||
@@ -62,22 +62,6 @@ export class Store<Target, Features extends AnyFeature<Target>[] = AnyFeature<Ta
|
||||
return this.#state.current;
|
||||
}
|
||||
|
||||
/** Subscribe to state changes. */
|
||||
subscribe(listener: (changedKeys: ReadonlySet<PropertyKey>) => void): () => void;
|
||||
subscribe<K extends keyof UnionFeatureState<Features>>(
|
||||
keys: K[],
|
||||
listener: (changedKeys: ReadonlySet<PropertyKey>) => void
|
||||
): () => void;
|
||||
subscribe(
|
||||
first: ((changedKeys: ReadonlySet<PropertyKey>) => void) | (keyof UnionFeatureState<Features>)[],
|
||||
second?: (changedKeys: ReadonlySet<PropertyKey>) => void
|
||||
): () => void {
|
||||
if (typeof first === 'function') {
|
||||
return this.#state.subscribe(first);
|
||||
}
|
||||
return this.#state.subscribe(first as (keyof (UnionFeatureState<Features> & object))[], second!);
|
||||
}
|
||||
|
||||
get request(): UnionFeatureRequests<Features> {
|
||||
return this.#request;
|
||||
}
|
||||
@@ -167,6 +151,24 @@ export class Store<Target, Features extends AnyFeature<Target>[] = AnyFeature<Ta
|
||||
// State
|
||||
// ----------------------------------------
|
||||
|
||||
/** Subscribe to state changes. */
|
||||
subscribe(callback: StateChange<UnionFeatureState<Features>>): () => void;
|
||||
|
||||
subscribe<K extends keyof UnionFeatureState<Features>>(
|
||||
keys: K[],
|
||||
callback: StateChange<UnionFeatureState<Features>, K>
|
||||
): () => void;
|
||||
|
||||
subscribe(
|
||||
first: StateChange<UnionFeatureState<Features>> | (keyof UnionFeatureState<Features>)[],
|
||||
second?: StateChange<UnionFeatureState<Features>>
|
||||
): () => void {
|
||||
return this.#state.subscribe(
|
||||
first as (keyof UnionFeatureState<Features>)[],
|
||||
second as StateChange<UnionFeatureState<Features>>
|
||||
);
|
||||
}
|
||||
|
||||
#syncAllFeatures(): void {
|
||||
const target = this.#target;
|
||||
if (!target) return;
|
||||
|
||||
+2
-2
@@ -2,8 +2,8 @@
|
||||
"compilerOptions": {
|
||||
"incremental": true,
|
||||
"composite": true,
|
||||
"target": "ES2020",
|
||||
"lib": ["ES2020"],
|
||||
"target": "ES2022",
|
||||
"lib": ["ES2022"],
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
|
||||
Reference in New Issue
Block a user