ople
v0.4.4
Published
[](https://www.npmjs.com/package/ople) [](https://travis-ci.org/alloc/ople) [: void
}
let nextId = 1
export const Todo = createClass<TodoState, TodoEvents, [TodoProps]>(
'Todo',
props => (todo, set, emit) => {
set(props)
set({
id: nextId++,
done: false,
})
auto(() =>
todo.done && emit('complete')
)
}
)
// The type for `Todo` is declared like so:
import {ReadonlyOpleObject} from 'ople'
export interface Todo extends ReadonlyOpleObject<TodoState, TodoEvents> {}
extends Ople
The Ople class can be extended by any class. This makes your class compatible with
Ople mixins, and it can even emit its own events.
The initOple function lets your class create reactions and event listeners with
automatic disposal. If you create an Ople object inside initOple's callback,
it will be attached to the Ople context, which means the attached Ople object will
have its dispose method called when the Ople context is disposed.
import {Ople, initOple, auto} from 'ople'
// Declare your props in TodoProps
interface TodoProps {
content: string
}
// Declare your events in TodoEvents
interface TodoEvents {
complete(): void
}
let nextId = 1
class Todo extends Ople<TodoEvents> {
id = nextId++
done = false
content!: string // The `!` is required to avoid `this.content = props.content` syntax
constructor(props: TodoProps) {
super()
// // This line is not needed, because of our `set(props)` call
// this.content = props.content
// Use the `initOple` function to set the Ople context, which
// handles the disposal of any listeners or reactions created
// inside the callback.
initOple(this, (self, set, emit) => {
// Merge the `props` object into `this`
set(props)
// Create a reactive callback
auto(() => self.done && emit('complete'))
// Attach listeners to `self` (or any other Ople object)
self.on({
complete() {
console.log('Todo completed:', self)
},
})
})
}
toggleDone() {
this.done = !this.done
}
}
const todo = new Todo({ content: 'Hello world' })
// BAD: Ople objects are readonly outside their initializer.
todo.done = true
// GOOD: Exposing a method is best practice.
todo.toggleDone()
Mixins
Mixins are functions that attach state, event listeners, and reactions to the
Ople context in which they are called. Any function can be a mixin, just like
any function can be a React hook.
export function mixin() {
// TODO: mixin example
}
const state = createOple(() => {
mixin()
})Ople also provides a few mixin helpers.
setEffect
The setEffect function lets you attach a disposable object to the current
Ople context, so the former is disposed of when the latter is, but not vice
versa.
You must provide an "owner" object as a cache key, in case you need the effect disposed of before the Ople context is disposed.
import {setEffect} from 'ople'
function subscribe(source, effect) {
let subscriber
setEffect(source, active => {
if (active) {
subscriber = source.subscribe(effect)
} else {
subscriber.dispose()
}
})
}Then, inside an Ople context, we can use our subscribe mixin.
// The `source` can be any object from your favorite library,
// assuming it has a `subscribe` method that returns a disposable.
const source = {}
const state = createOple((self, set, emit) => {
subscribe(source, value => {
console.log('Source changed:', value)
})
})setState
The setState function is made for mixins that need to update state
by merging patch objects.
import {setState} from 'ople'
function makeUndoable() {
// Expose state and/or methods
setState({
history: [],
undo() {...},
redo() {...},
})
}getOple and expectOple
These functions are for accessing the current Ople context. Use getOple
if you want to check for null manually. Otherwise, use expectOple to
throw an error when no Ople context is active.
import {expectOple} from 'ople'
interface MixinState {
foo: number
}
interface MixinEvents {
bar(): void
}
function mixin() {
const self = expectOple<MixinState, MixinEvents>()
self.set({ foo: 0 })
self.on('bar', () => {
console.log('bar!')
})
}