Control flow
If statements
If blocks work seamlessly with Ripple's templating language, you can put them inside the JSX-like statements, making control-flow far easier to read and reason with.
export component Truthy({ x }) {
<div>
if (x) {
<span>{'x is truthy'}</span>
} else {
<span>{'x is falsy'}</span>
}
</div>
}Switch statements
Switch statements let you conditionally render content based on a value. They work with both static and reactive values.
export component StatusIndicator({ status }) {
<div>
switch (status) {
case 'loading':
<p>{'Loading...'}</p>
break;
case 'success':
<p>{'Success!'}</p>
break;
case 'error':
<p>{'Error!'}</p>
break;
default:
<p>{'Unknown status'}</p>
}
</div>
}You can also use reactive values with switch statements.
import { track } from 'ripple';
export component InteractiveStatus() {
let status = track('loading');
<button onClick={() => @status = 'success'}>{'Success'}</button>
<button onClick={() => @status = 'error'}>{'Error'}</button>
<div>
switch (@status) {
case 'loading':
<p>{'Loading...'}</p>
break;
case 'success':
<p>{'Success!'}</p>
break;
case 'error':
<p>{'Error!'}</p>
break;
default:
<p>{'Unknown status'}</p>
}
</div>
}For statements
You can render collections using a for...of loop.
component ListView({ title, items }) {
<h2>{title}</h2>
<ul>
for (const item of items) {
<li>{item.text}</li>
}
</ul>
}
// usage
export default component App() {
<ListView
title="My List"
items={[
{ text: "Item 1" },
{ text: "Item 2" },
{ text: "Item 3" },
]}
/>
}The for...of loop has also a built-in support for accessing the loops numerical index. The label index declares a variable that will used to assign the loop's index.
for (const item of items; index i) {
<div>{item.label}{' at index '}{i}</div>
}You can also provide a key for efficient list updates and reconciliation:
for (const item of items; index i; key item.id) {
<div>{item.label}{' at index '}{i}</div>
}Key Usage Guidelines:
- Arrays with
#{}objects: Keys are usually unnecessary - object identity and reactivity handle updates automatically. Identity-based loops are more efficient with less bookkeeping. - Arrays with plain objects: Keys are needed when object reference isn't sufficient for identification. Use stable identifiers:
key item.id.
You can use Ripple's reactive arrays to easily compose contents of an array.
import { TrackedArray } from 'ripple';
export component Numbers() {
const array = new TrackedArray(1, 2, 3);
for (const item of array; index i) {
<div>{item}{' at index '}{i}</div>
}
<button onClick={() => array.push(array.length + 1)}>{"Add Item"}</button>
}Clicking the <button> will create a new item.
Note
for...of loops inside components must contain either dom elements or components. Otherwise, the loop can be run inside an effect or function.
Try statements
Try blocks work to build the foundation for error boundaries, when the runtime encounters an error in the try block, you can easily render a fallback in the catch block.
import { reportError } from 'some-library';
export component ErrorBoundary() {
<div>
try {
<ComponentThatFails />
} catch (e) {
reportError(e);
<div>{'An error occurred! ' + e.message}</div>
}
</div>
}Dynamic Elements
You can render dynamic HTML elements by storing the tag name in a tracked variable and using the <@tagName> syntax:
export component App() {
let tag = track('div');
<@tag class="dynamic">{'Hello World'}</@tag>
<button onClick={() => @tag = @tag === 'div' ? 'span' : 'div'}>{'Toggle Element'}</button>
}Async (Suspense boundaries) Experimental
export component SuspenseBoundary() {
try {
<AsyncComponent />
} pending {
<p>{'Loading...'}</p> // fallback
}
}