Skip to main content

Conditional Mounting

mounted attribute

Each element and component has possibility to be mounted/unmounted at any time controlled from "outside".

function ColorApp() {
const mounted$ = window.when("pointermove").map(event => !!event.x)

return (
<div mounted={mounted$}>Visible</div>
)
}

<ColorApp mounted={new State(false)} />
note

Passing plain values to mounted is not allowed, it clearly indicates a mistake as it will never be changed.

Guard Pattern

Guard pattern usually refers to a Return/Throw Guard in JavaScript.

However, in Proton it is a predicate, which defines if an object is ok to use. If the valid guard return false, normally it is simply skipped.

const plain = { value: 1 }
const guarded = { value: 1, valid(value) { return value > 0 } }

This very mechanism is used to tell if an element should be mounted or unmounted by invoking valid function.

In case of Observable Guarded object, it will call the guard on each update.

JSX Attribute Guarding

Each JSX attribute can be a Mounting Guard, which means the element is mounted only when every attribute is NOT guarded.

TypeScript: This approach allows type narrowing, so your property is always getting a correct type.

you can use guard method or built-in predicates like required.

const className = new State("")
const content = new State<string | null>(null)

const Component = () => (
<>
<span className={Proton.guard.avoid(className)}>{Proton.guard.require(content)}</span>
<span className={Proton.guard(className, x => !x)}>{Proton.guard(content, x => x)}</span> // Equal Alternative.
</>
)

As you can see even the children can be guarded. The guard method can be implemented by your State library or you can create your own utility function do that to cover your special cases.

tip

If you dislike this approach, you can implement the way SolidJS does it or your unique way by Extending Default Behavior.

Real-World Example

A component example I converted from React and changed a bit for demonstration purposes.

import { State, StateBoolean, StateOrPlain } from "@denshya/reactive"


interface MiniProfileProps {
user: StateOrPlain<User>
}

function MiniProfile(props: MiniProfileProps) {
const user = State.from(props.user)

const inputValue = new State("")
const inputMounted = new StateBoolean(false)

inputValue.sets(user.$.avatar)

return (
<div className="mini-profile">
<div className="mini-profile__profile">
<button className="mini-profile__letter" mounted={user.$.avatar.is(null)}>
<ColoredLetter letter={user.$.firstName.$[0]} />
</button>
<img className="mini-profile__avatar" src={Proton.guard.require(user.$.avatar)} alt="avatar" />
<input value={inputValue} mounted={inputMounted} />
<div className="mini-profile__info">
<div className="mini-profile__name">{user.$.firstName} {user.$.lastName.$[0]}.</div>
<div className="mini-profile__email">{user.$.email}</div>
</div>
</div>
<button type="button" on={{ click: () => inputMounted.toggle() }}>
<Icon name="pen" />
</button>
</div>
)
}