Skip to content

Commit

Permalink
feat: add observable()
Browse files Browse the repository at this point in the history
  • Loading branch information
streamich committed Feb 6, 2018
1 parent 0b810ea commit c60b68a
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 0 deletions.
63 changes: 63 additions & 0 deletions src/context/__tests__/observable.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import {observable} from '../observable';

describe('observable()', () => {
it('creates and object', () => {
const obs = observable(123);
expect(typeof obs).toBe('object');
});
it('if no initial value provided state equals undefined', () => {
const obs = observable();
expect(obs.get()).toBe(void 0);
});
it('sets state to the default specified value, .get() returns it', () => {
const state = {};
const obs = observable(state);
expect(obs.get()).toBe(state);
});
it('returns mutated state', () => {
const obs = observable();
const state = {};
obs.set(state);
expect(obs.get()).toBe(state);
});
it('.sub() returns an unsub function', () => {
const obs = observable();
const unsub = obs.sub(() => {});
expect(typeof unsub).toBe('function');
});
it('calls listener on on state change', () => {
const listener = jest.fn();
const obs = observable();
obs.sub(listener);
obs.set(123);
expect(listener).toHaveBeenCalledTimes(1);
});
it('calls correct number of times', () => {
const listener = jest.fn();
const obs = observable();
obs.sub(listener);
obs.set(1);
obs.set(2);
obs.set(3);
expect(listener).toHaveBeenCalledTimes(3);
});
it('calls listener with the correct data', () => {
const listener = jest.fn();
const obs = observable();
obs.sub(listener);
obs.set(1);
obs.set(2);
obs.set(3);
expect(listener.mock.calls).toEqual([[1], [2], [3]]);
});
it('stops calling listener when unsubscribed', () => {
const listener = jest.fn();
const obs = observable();
const unsub = obs.sub(listener);
obs.set(1);
obs.set(2);
unsub();
obs.set(3);
expect(listener.mock.calls).toEqual([[1], [2]]);
});
});
31 changes: 31 additions & 0 deletions src/context/observable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
export type TObservalbeUnsub = (() => void);
export type TObservableSet<T> = (state: T) => void;
export interface IObservable<T> {
get: () => T;
set: TObservableSet<T>;
sub: (listener: TObservableSet<T>) => TObservalbeUnsub;
}

export const observable: <T>(state?: T) => IObservable<T> = state => {
let listeners = [];
let currentState = state;

const get = () => currentState;

const set = state => {
currentState = state;
for (const listener of listeners) listener(currentState);
};

const sub = listener => {
listeners.push(listener);

return () => (listeners = listeners.filter(item => item !== listener));
};

return {
get,
set,
sub,
};
};

0 comments on commit c60b68a

Please sign in to comment.