Event-driven, observable data flow for React ๐ฅ๐
Features
- Transparent observability for seamless, reactive data flow
- Automatic disposal of event listeners and
auto
reactions - Compatible with all
wana
functions - Mixins for sharable behavior (inspired by React hooks)
- Strict type safety with TypeScript
- Objects are readonly outside their initializer
- Objects have built-in event emitting
- Highly minifiable code
- Concise, distilled API
ย
The createOple
function constructs an Ople object, which is both observable
and readonly to React components. Expose methods for React components to call,
and expose events for React components to subscribe to.
import {createOple, auto} from 'ople'
// Pass an initializer function to receive a state object,
// a set function, and an emit function.
const state = createOple((self, set, emit) => {
// The state is mutable.
self.a = 1
// The state is observable.
auto(() => {
console.log('a:', self.a)
})
// The set function is a shortcut for `Object.assign(self, {...})`
set({ b: 1, c: 1 })
// The set function converts every getter into an observable getter (unless a setter exists).
set({
get sum() {
return self.a + self.b + self.c
}
})
auto(() => {
console.log('sum:', self.sum)
})
// The set function is the recommended way of declaring methods.
set({
// Methods declared with `set` are wrapped to disable implicit observation
// and to set the Ople context until the method returns.
add(key: string, n: number) {
self[key] += n
// The emit function is a shortcut for `self.emit(...)`
emit('add', key, n)
}
})
// Subscribe to your own events or the events of another Ople object.
// Your listeners are removed when the built-in `dispose` method is called.
self.on({
add(key, n) {
console.log('add:', key, n)
}
})
})
// The state is observable outside the initializer, too.
auto(() => {
console.log('b:', state.b)
})
state.add('b', 2)
// Clean up any side effects.
state.dispose()
ย
If you like creating objects with new
syntax or you prefer storing your methods
on a prototype for efficiency, you can create an Ople
subclass. For more info,
see the Classes page.
ย
As an alternative to classes (which limit you to a single superclass), you can
use "mixin functions" to share behavior between your Ople objects. Mixins should
be very familiar to anyone who uses React hooks. Just remember, mixins are NOT
subject to the same rules as React hooks. For example, mixins can be called from
within if
blocks. The only rule of mixins is that you should only ever call
them from within an Ople context (eg: inside a createOple
initializer).
For more info, see the Mixins page.
ย
These questions are common for beginners to ask. If you have a question not yet listed here, feel free to open a pull request, and I'll add an answer to it.
-
Why are Ople objects readonly outside their initializer?
To keep mutations local to the state they are acting upon. This makes it easy to remember which module contains the code you are concerned with. It also encourages you to declare reusable methods instead of duplicating logic across your project. Your methods will be implicitly bound to their local state, which means you won't have to manually
.bind
them before using them as React element props (eg:onClick
).