![Oracle Drags Its Feet in the JavaScript Trademark Dispute](https://cdn.sanity.io/images/cgdhsj6q/production/919c3b22c24f93884c548d60cbb338e819ff2435-1024x1024.webp?w=400&fit=max&auto=format)
Security News
Oracle Drags Its Feet in the JavaScript Trademark Dispute
Oracle seeks to dismiss fraud claims in the JavaScript trademark dispute, delaying the case and avoiding questions about its right to the name.
Fast Virtual DOM components with Reactive updating.
class
/ this
nonsensedompteuse
adds the concept of encapsulated components to pure functional virtual dom.
Standard Virtual nodes and components are composed to build a Vnode tree that can scale in size and complexity.
A component is simply a function that takes an option object as an argument and returns a Vnode ready to be used inside its parent children.
Note: typescript will be used in the examples, javascript devs can ignore the types annotations.
import { Component } from 'dompteuse'
export default function(props?: Props) {
return Component({
key: 'Select',
props,
defaultProps,
initState,
connect,
render
})
}
Let's look at the option object properties:
Mandatory String
This is the standard Virtual node key used to uniquely identify this Vnode. It is also used for logging purposes, so it is usually just the name of the component.
Optional Object
An object representing all the properties passed by our parent.
Typically props either represent state that is maintained outside the component or properties used to tweak the component's behavior.
The render
function will be called if the props object changed shallowly, hence it's a good practice to use a flat object.
Note: props and state are separated exactly like in React
as it works great. The same design best practices apply.
Optional Object
(upper type of props)
An object with part of the prop keys that should be used if the parent do not specify all the props.
Mandatory Object
A function taking the initial props as an argument and returning the starting state.
Mandatory function({ on, messages, props }: ConnectParams<Props, State>): void
Connects the component to the app and computes the local state of the component.
connect
is called only once when the component is mounted.
connect
is called with three arguments, encapsulated in a ConnectParams
object:
on
registers a Stream that modifies the component local state. The stream will be automatically unsubscribed from when
the component is unmounted.messages
is the interface used to listen
to custom Messages sent by direct component children or send
a message to our direct component parent. Streams created this way will also be unsubscribed from when the component is unmounted.props
A props accessor function. Used to read props inside connect.connect
arguably has more interesting characteristics than the imperative approach React
use (i.e setState
):
Streams are composable, callbacks are not. Doing things like throttling or only listening to the very last ajax action fired is a recurrent, non abstractable pain with imperative callback/setState.
Callback references often change over time (most likely from using partial application) and we can no longer apply streamlined performance optimizations because some props truly represent data while other props are callbacks that may or may not purposely change. By using simple Messages
instead of bound functions/closures, this issue is avoided.
Example:
import { Message, ConnectParams } from 'dompteuse'
// Message used to communicate with our children in a cohesive manner
const TriggerClick = Message('triggerClick')
function connect({ on, props, messages }: ConnectParams<Props, State>) {
// Subscribe to the stream of button clicks and update our state every time it changes
on(messages.listen(TriggerClick), state => {
const opened = !state.opened
// Any 'on' handler must return the new component state
return merge(state, { opened })
})
}
on
can listen to any kind of most
stream. See global streams.
Just like with props, a redraw will only get scheduled if the state object changed shallowly so returning the current state
in on()
will skip rendering.
Mandatory function(props: Props, state: State): VNode
Returns the Vnode tree based on the props and state.
Example:
import { h, Message } from 'dompteuse'
interface State {
text: string
}
const ButtonClick = Message('buttonClick')
function render(props: void, state: State) {
const { text } = state
return h('div#text', [
h('h1', 'Hello'),
h('p', text),
h('button', { events: { onClick: ButtonClick } })
])
}
A construct is provided to easily build push-based global streams in a typesafe fashion. This is entirely optional.
You typically want to keep very transient state as local as possible so that it remains encapsulated in a component and do not leak up.
Example of typical local state
Additionally, keeping state that is only useful to one screen should be kept inside the top-most component of that screen and no higher.
That leaves global state, which can be updated from anywhere and is read from multiple screens.
Example of typical global state
Example:
import { Message, GlobalStream } from 'dompteuse'
import merge from './util/obj/merge'
export const setUserName = Message<string>('setUserName')
interface UserState {
name: string
}
const initialState = { name: 'bob' }
// This exports a stream ready to be used in a component's connect function
export default GlobalStream<UserState>(initialState, on => {
on(setUserName, (state, name) =>
merge(state, { name })
)
})
// ...
// Subscribe to it in a component's connect
import userState from './userState'
// Provide an initial value
function initialState() {
return {
userName: userState.value.name
}
}
connect(on: StreamSub<State>, events: Events) {
on(userState, (state, user) => {
// 'Copy' the global user name into our local component state to make it available to `render`
return merge(state, { userName: user.name })
})
}
// ...
// Then anywhere else, import the stream and the message
stream.send(setUserName('Monique'))
Creates a Vnode
This is proxied to snabbdom's h so we can add our type definitions
transparently.
import { h } from 'dompteuse'
h('div', 'hello')
On top of the snabbdom
modules you pass to startApp
, an extra module is installed by dompteuse
: events
.
import { Message } from 'dompteuse'
const SomeMessage = Message<Event>('someMessage')
// Send a message to the enclosing component on click
h('div', { events: { onClick: SomeMessage } })
// Or prepare the message to be sent with an argument.
// This is more efficient than creating a closure on every render.
const AnotherMessage = Message<{x: number}>('anotherMessage')
h('div', { events: { onClick: AnotherMessage.with({ x: 3 }) } })
Performs the initial render of the app synchronously.
function startApp<S>(options: {
app: Vnode // The root Vnode
elm: HTMLElement // The root element where the app will be rendered
snabbdomModules: any[] // The snabbdom modules that should be active during patching
}): void;
import { startApp } from 'dompteuse'
import app from './app'
declare var require: any
const snabbdomModules = [
require('snabbdom/modules/class'),
require('snabbdom/modules/props'),
require('snabbdom/modules/attributes'),
require('snabbdom/modules/style')
]
startApp({ app, snabbdomModules, elm: document.body })
Create a custom application message used to either communicate between components or push to a GlobalStream.
import { Message } from 'dompteuse'
// Message taking no arguments
const increment = Message('increment')
// Message taking one argument
const incrementBy = Message<number>('incrementBy')
Used to subscribe to a stream and update the component state.
If nothing is returned (undefined) then the component state is not modified.
Signature:
on<A>(stream: Stream<A>, cb: (state: S, value: A) => S|void): Stream<A>
on(message: NoArgMessage, cb: (state: S) => S|void): Stream<void>
// Shortcut for on(messages.listen(MyMessage))
on<P>(message: Message<P>, cb: (state: S, payload: P) => S|void): Stream<P>
// Listen for messages coming from immediate Vnodes or component children
messages.listen<P>(message: Message<P>): Stream<P>
// Listen for a Message at the first Element found using the given CSS selector.
// This should be rarely used, and is mostly useful for components
// that attach their content to a remote DOM Element, like popups.
messages.listenAt<P>(selector: string, message: Message<P>): Stream<P>;
// Sends a message to the nearest parent component
messages.send<P>(message: MessagePayload<P>): void
Example:
import { ConnectParams, Message } from 'dompteuse'
import { Opened } from './someComponent'
const Increment = Message('increment')
connect({ on, messages }: ConnectParams<Props, State>) {
// Listen to the Opened even sent by 'someComponent'
const openStream = messages.listen(Opened)
// This is equivalent to above
const openStream2 = on(Opened, state => state)
// Send a message. Our direct parent can react to it.
messages.send(Increment())
}
Builds a Stream of DOM Events using Event delegation.
listenAt(node: Element, targetSelector: string, eventName: string): Stream<Event>
import { Events } from 'dompteuse'
const stream = Events.listenAt(document.body, '.button', 'click')
The snabbdom
patch function that dompteuse uses. Only available after the call to startApp
import { patch } from 'dompteuse'
FAQs
Virtual dom, observables / streams and isolated components
The npm package dompteuse receives a total of 5 weekly downloads. As such, dompteuse popularity was classified as not popular.
We found that dompteuse demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Oracle seeks to dismiss fraud claims in the JavaScript trademark dispute, delaying the case and avoiding questions about its right to the name.
Security News
The Linux Foundation is warning open source developers that compliance with global sanctions is mandatory, highlighting legal risks and restrictions on contributions.
Security News
Maven Central now validates Sigstore signatures, making it easier for developers to verify the provenance of Java packages.