Skip to content

Commit

Permalink
dont expose key and previousValue by default, pull them out into .bef…
Browse files Browse the repository at this point in the history
…ore()/.beforeAll()
  • Loading branch information
bcherny committed Nov 7, 2017
1 parent d0b1229 commit b8f4d82
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 17 deletions.
25 changes: 24 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ Though Babydux automatically updates your model for you, it also lets you listen
```tsx
store
.on('today')
.map(_ => _.value)
.filter(() => _.getTime() % 2 === 0) // only even timestamps
.debounce(100)
.subscribe(_ => console.log('Date changed', _))
Expand Down Expand Up @@ -116,6 +115,30 @@ And logs look like this:

<img src="logger.png" width="895" />

### Plugins

Babydux is easy to modify with plugins (also called "higher order stores"). Just define a function that takes a store as an argument and returns a store, adding listeners along the way. For convenience, Babydux supports 2 types of listeners for plugins:

```ts
import { createStore, Plugin } from 'babydux'

let withLocalStorage: Plugin = store => {

// Listen on an event
store.onAll().subscribe(_ =>
console.log('something changed!', _)
)

// Listen on an event (fires before the model is updated)
store.beforeAll().subscribe(({ key, previousValue, value}) =>
localStorage.set(key, value)
)

}
```

*Babydux also supports `.on()` and `.before()`, but because a plugin doesn't know what Actions will be bound to it, these are generally unsafe to use.*

## Design philosophy

**Goal #1 is total type-safety.**
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "babydux",
"version": "2.0.0",
"version": "2.0.1",
"description": "A paper-thin, 100% typesafe Redux for babies",
"main": "dist/src/index.js",
"typings": "dist/src/index.d.ts",
Expand Down
12 changes: 10 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,27 @@ export type Babydux<Actions extends object> = {
}
}

export class Store<Actions extends object> extends Emitter<Babydux<Actions>> {
export class Store<Actions extends object> extends Emitter<Actions> {
private befores = new Emitter<Babydux<Actions>>()
private emitter = new Emitter<Actions>()
constructor(private state: Actions) {
super()

for (let key in state) {
this.emitter.on(key).subscribe(value => {
let previousValue = state[key]
this.befores.emit(key, { key, previousValue, value })
state[key] = value
this.emit(key, { key, previousValue, value })
this.emit(key, value)
})
}
}
before<K extends keyof Actions>(key: K) {
return this.befores.on(key)
}
beforeAll<K extends keyof Actions>() {
return this.befores.all()
}
get<K extends keyof Actions>(key: K) {
return this.state[key]
}
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Plugin, Store } from '../'

export let withLogger: Plugin = store => {

store.all().subscribe(({ key, previousValue, value }) => {
store.beforeAll().subscribe(({ key, previousValue, value }) => {
console.info(`%c ⥁ ${key}`, 'background-color: rgb(96, 125, 139); color: #fff; padding: 2px 8px 2px 0;', previousValue, '→', value)
})

Expand Down
38 changes: 29 additions & 9 deletions test/stateful.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,17 +66,37 @@ test('[stateful] it should support lenses', t =>
test('[stateful] it should support effects', t =>
withElement(MyComponentWithLens, _ => {
t.plan(1)
store.on('isTrue').subscribe(({ value }) => t.is(value, false))
store.on('isTrue').subscribe(_ => t.is(_, false))
Simulate.click(_.querySelector('button')!)
})
)

test('[stateful] it should call .subscribe(..) with the key, current value, and previous value', t =>
withElement(MyComponentWithLens, _ => {
t.plan(1)
store.on('isTrue').subscribe(_ =>
t.deepEqual(_, { key: 'isTrue', previousValue: false, value: true })
)
Simulate.click(_.querySelector('button')!)
})
test('[stateful] it should call .beforeAll().subscribe() with the key, current value, and previous value', t =>
withElement(MyComponentWithLens, _ => {
t.plan(1)
store.beforeAll().subscribe(_ =>
t.deepEqual(_, { key: 'isTrue', previousValue: false, value: true })
)
Simulate.click(_.querySelector('button')!)
})
)

test('[stateful] it should call .before().subscribe() with the key, current value, and previous value', t =>
withElement(MyComponentWithLens, _ => {
t.plan(1)
store.before('isTrue').subscribe(_ =>
t.deepEqual(_, { key: 'isTrue', previousValue: true, value: false })
)
Simulate.click(_.querySelector('button')!)
})
)

test('[stateful] it should call .on().subscribe() with the current value', t =>
withElement(MyComponentWithLens, _ => {
t.plan(1)
store.on('isTrue').subscribe(_ =>
t.is(_, true)
)
Simulate.click(_.querySelector('button')!)
})
)
26 changes: 23 additions & 3 deletions test/stateless.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,37 @@ test('[stateless] it should support lenses', t =>
test('[stateless] it should support effects', t =>
withElement(MyComponentWithLens, _ => {
t.plan(1)
store.on('isTrue').subscribe(({ value }) => t.is(value, false))
store.on('isTrue').subscribe(_ => t.is(_, false))
Simulate.click(_.querySelector('button')!)
})
)

test('[stateless] it should call .subscribe(..) with the key, current value, and previous value', t =>
test('[stateless] it should call .beforeAll().subscribe() with the key, current value, and previous value', t =>
withElement(MyComponentWithLens, _ => {
t.plan(1)
store.on('isTrue').subscribe(_ =>
store.beforeAll().subscribe(_ =>
t.deepEqual(_, { key: 'isTrue', previousValue: false, value: true })
)
Simulate.click(_.querySelector('button')!)
})
)

test('[stateless] it should call .before().subscribe() with the key, current value, and previous value', t =>
withElement(MyComponentWithLens, _ => {
t.plan(1)
store.before('isTrue').subscribe(_ =>
t.deepEqual(_, { key: 'isTrue', previousValue: true, value: false })
)
Simulate.click(_.querySelector('button')!)
})
)

test('[stateless] it should call .on().subscribe() with the current value', t =>
withElement(MyComponentWithLens, _ => {
t.plan(1)
store.on('isTrue').subscribe(_ =>
t.is(_, true)
)
Simulate.click(_.querySelector('button')!)
})
)

0 comments on commit b8f4d82

Please sign in to comment.