import { BehaviorSubject, Observable } from "rxjs"; // basically a wrapper for BehaviorSubject export class MutableObservable<T> { private Subject: BehaviorSubject<T>; constructor(value: T) { this.Subject = new BehaviorSubject<T>(value); this.getValue = this.getValue.bind(this); this.setValue = this.setValue.bind(this); this.asObservable = this.asObservable.bind(this); } getValue(): T { return this.Subject.getValue(); } setValue(value: T): void { return this.Subject.next(value); } asObservable() { return this.Subject.asObservable(); } } export class MutableObservableCollection<T extends { key: string | number }> { private subject: BehaviorSubject<T[]>; constructor(initialItems: T[] = []) { this.subject = new BehaviorSubject<T[]>(initialItems); } getItems(): T[] { return this.subject.getValue(); } asObservable(): Observable<T[]> { return this.subject.asObservable(); } setItems(newItems: T[]): void { this.subject.next(newItems); } updateItem(updatedItem: T): void { this.subject.next( this.getItems().map(item => item.key === updatedItem.key ? updatedItem : item) ); } addItem(newItem: T): void { this.subject.next([...this.getItems(), newItem]); } removeItem(key: string | number): void { this.subject.next(this.getItems().filter(item => item.key !== key)); } moveItemUp(key: string | number): void { const items = this.getItems(); const index = items.findIndex(item => item.key === key); // Als het item niet bestaat of al bovenaan staat, doe niets if (index <= 0) return; // Verwissel het item met het item ervoor const newItems = [...items]; [newItems[index - 1], newItems[index]] = [newItems[index], newItems[index - 1]]; this.subject.next(newItems); } moveItemDown(key: string | number): void { const items = this.getItems(); const index = items.findIndex(item => item.key === key); // Als het item niet bestaat of al onderaan staat, doe niets if (index === -1 || index >= items.length - 1) return; // Verwissel het item met het item erna const newItems = [...items]; [newItems[index], newItems[index + 1]] = [newItems[index + 1], newItems[index]]; this.subject.next(newItems); } getChangedItems(originalItems: T[]): T[] { return this.getItems().filter(item => { const original = originalItems.find(o => o.key === item.key); return original && JSON.stringify(original) !== JSON.stringify(item); }); } }
React
export function useMutableObservable<T>(subjectInput: MutableObservable<T>): [T, (value: T) => any, (changes: Partial<T>) => any] { const state = useMemo(() => ({ observable: subjectInput.asObservable(), setValue: subjectInput.setValue, getValue: subjectInput.getValue, updateValue: (_changes: Partial<T>) => 0 }), [subjectInput]); const [result, setResult] = useState<[T, (value: T) => any, (changes: Partial<T>) => any]>([state.getValue(), state.setValue, state.updateValue]); const resultRef = useRef(state.getValue()); resultRef.current = state.getValue(); useEffect(() => { if (!state?.observable) return; const subscription = state.observable.subscribe((x: any) => { if (x === resultRef.current) return; setResult([x, state.setValue, state.updateValue]); }); return () => subscription.unsubscribe(); }, [state]); return result; } export function useObservable<T>(subject: Observable<T | undefined>, initialValue?: T): T | undefined { const [data, setData] = useState<T | undefined>(initialValue); useEffect(() => { if (!subject) { return; } const subscription = subject.subscribe((x: any) => { setData(x); }); return () => subscription.unsubscribe(); }, [subject]); return data; }
WIP
import { Observable, Observer, Subject, Subscription } from "rxjs"; export interface ObservableValue<T> { getValue(): T; setValue(value: T): void; asObservable(): Observable<T>; subscribe( next?: ((value: T) => void) | null, error?: ((error: any) => void) | null, complete?: (() => void) | null ): Subscription; } /** * Maakt een gecontroleerde observable waarde met toegang tot de huidige waarde. * Emit geen waarde automatisch bij constructie (zoals BehaviorSubject zou doen), * maar houdt wel de laatste waarde bij. */ export function makeObservableValue<T>(initialValue: T): ObservableValue<T> { let currentValue = initialValue; const subject = new Subject<T>(); const o = subject.asObservable(); const subscribe = o.subscribe.bind(o); return { getValue: () => currentValue, setValue: (value: T) => { console.log("setValue", value); currentValue = value; subject.next(value); }, asObservable: () => subject.asObservable(), subscribe: subscribe //(next, error, complete) => subject.subscribe({ next, error, complete } as Partial<Observer<T>>), }; } export class MutableObservableCollection<T extends { key: string | number }> { private ov: ObservableValue<T[]>; constructor(initialItems: T[] = []) { this.ov = makeObservableValue(initialItems); } getItems(): T[] { return [...this.ov.getValue()]; } asObservable(): Observable<T[]> { return this.ov.asObservable(); } asObservableValue(): ObservableValue<T[]> { return this.ov; } setItems(newItems: T[]): void { this.ov.setValue(newItems); } updateItem(updatedItem: T): void { this.setItems(this.getItems().map(item => item.key === updatedItem.key ? updatedItem : item)); } addItem(newItem: T): void { this.setItems([...this.getItems(), newItem]); } removeItem(key: string | number): void { const newItems = this.getItems().filter(item => item.key !== key); this.setItems(newItems); } getItem(key: string | number): T | undefined { return this.getItems().find(item => item.key === key); } moveItemUp(key: string | number): void { const items = this.getItems(); const index = items.findIndex(item => item.key === key); // Als het item niet bestaat of al bovenaan staat, doe niets if (index <= 0) return; // Verwissel het item met het item ervoor const newItems = [...items]; [newItems[index - 1], newItems[index]] = [newItems[index], newItems[index - 1]]; this.setItems(newItems); } moveItemDown(key: string | number): void { const items = this.getItems(); const index = items.findIndex(item => item.key === key); // Als het item niet bestaat of al onderaan staat, doe niets if (index === -1 || index >= items.length - 1) return; // Verwissel het item met het item erna const newItems = [...items]; [newItems[index], newItems[index + 1]] = [newItems[index + 1], newItems[index]]; this.setItems(newItems); } getChangedItems(originalItems: T[]): T[] { return this.getItems().filter(item => { const original = originalItems.find(o => o.key === item.key); return original && JSON.stringify(original) !== JSON.stringify(item); }); } } // react hooks: export function useObservableValue<T>(subject: ObservableValue<T>): T { const [, forceUpdate] = useReducer(x => x + 1, 0); useEffect(() => { if (!subject) { return; } const subscription = subject.subscribe(forceUpdate); return () => subscription.unsubscribe(); }, [subject]); return subject.getValue(); } export function useObservableValueDebug<T>(subject: ObservableValue<T>, key: string): T { const [, forceUpdate] = useReducer(x => x + 1, 0); useEffect(() => { if (!subject) { return; } console.log('observable-value subscribe', key); const subscription = subject.subscribe((x: T) => { console.log('observable-value changed', key, x); forceUpdate(); }); return () => { console.log('observable-value unsubscribe', key); subscription.unsubscribe(); }; }, [subject, key]); console.log('observable-value render', key, subject.getValue()); return subject.getValue(); }
941000cookie-checkTypescript MutableObservable and MutableObservableCollection