Skip to main content

Create Abyss App Deprecated

Starting an app with `create-abyss-app` is deprecated. Please refer to our Templates documentation for the recommended setup path.
View Templates

Updating Parcels

Updating Parcels

Properties are useful for initial configuration when a Parcel is first mounted, but updating properties after mount will not trigger a remount or cause the Parcel to update by default.

This default behavior prevents common issues such as:

  • State Loss: Internal state (form inputs, scroll positions, user interactions) would reset on every property change
  • Performance Impact: Unmounting and remounting is computationally expensive, especially for complex Parcels
  • UI Flickering: Users would experience visual flickering as components rebuild

For runtime communication between your host application and Parcels, use the event-based pattern:

In the Parcel: Listen for events using the useRegisterEventListener hook or registerEventListener tool

In the Host: Send updates using the dispatchEvent tool

This approach keeps the Parcel mounted, preserves its internal state, and updates only the necessary data.

Enabling property-based remounting

If you need property changes to trigger a full Parcel remount, you can enable the remounting behavior by setting the enableMutationObserver property to true in the settings.json file as shown here.

Warning

This approach is not recommended for most use cases. The event-based communication pattern provides better performance and user experience.

Here's a complete example demonstrating how to use properties for initial configuration and events for runtime updates:

Parcel component with event listener

The Parcel initializes with the initial-count prop, then listens for runtime updates via the cartUpdate event.

// Example Parcel file
import React, { useState, useCallback, useEffect } from 'react';
import { Button } from '@uhg-abyss/web/ui/Button';
import { createTheme } from '@uhg-abyss/web/tools/theme';
import { ThemeProvider } from '@uhg-abyss/web/ui/ThemeProvider';
import { useRegisterEventListener } from '@uhg-abyss/parcels/hooks/useRegisterEventListener';
const theme = createTheme('uhc');
export const ParcelApp = (args) => {
const [cartCount, setCartCount] = useState(args['initial-count']);
// Event handler for cart updates
const handleCartUpdate = useCallback((event) => {
setCartCount(event.detail.count);
}, []);
/**
* STORYBOOK DEVELOPMENT ONLY:
* Syncs state with args['initial-count'] when Controls change.
* Not needed in production because args never change at runtime.
*/
useEffect(() => {
if ((window as any).__STORYBOOK_PREVIEW__) {
setCartCount(args?.['initial-count']);
}
}, [args?.['initial-count']]);
// Listen for 'cartUpdate' events from the host
useRegisterEventListener('cartUpdate', handleCartUpdate);
return (
<ThemeProvider theme={theme}>
<Button>🛒 Cart Count: {cartCount}</Button>
</ThemeProvider>
);
};

Host application dispatching events

The host application sends updates to the Parcel using the dispatchEvent tool.

// Example host/web file
import React, { useState } from 'react';
import { dispatchEvent } from '@uhg-abyss/parcels/tools/dispatchEvent';
import { Button } from '@uhg-abyss/web/ui/Button';
import { Layout } from '@uhg-abyss/web/ui/Layout';
export const HelloAbyss = () => {
const [cartCount, setCartCount] = useState(5);
const addToCart = () => {
const newCount = cartCount + 1;
setCartCount(newCount);
dispatchEvent('cartUpdate', { count: newCount });
};
const removeFromCart = () => {
const newCount = Math.max(cartCount - 1, 0);
setCartCount(newCount);
dispatchEvent('cartUpdate', { count: newCount });
};
return (
<Layout.Stack>
<Button onClick={addToCart}>Add Item</Button>
<Button onClick={removeFromCart}>Remove Item</Button>
<abyss-parcel import="parcel-app@test" initial-count={5} />
</Layout.Stack>
);
};

In this example:

  • The Parcel is initialized with initial-count={5}
  • As the user clicks the buttons, the host dispatches cartUpdate events
  • The Parcel's event listener updates its internal state without remounting
  • User interactions and component state are preserved throughout

Updating via property changes

In this example, the Parcel depends entirely on the cart-count property for updates. When the host changes the property value, the Parcel does not automatically update unless remounting is enabled (via enableMutationObserver).

Parcel component relying on property changes

// Example Parcel file
import React from 'react';
import { Button } from '@uhg-abyss/web/ui/Button';
import { createTheme } from '@uhg-abyss/web/tools/theme';
import { ThemeProvider } from '@uhg-abyss/web/ui/ThemeProvider';
const theme = createTheme('uhc');
export const ParcelApp = (args) => {
const cartCount = args?.count;
return (
<ThemeProvider theme={theme}>
<Button>🛒 Cart Count: {cartCount}</Button>
</ThemeProvider>
);
};

Host application attempting runtime updates via property changes

// Example host/web file
import React, { useState } from 'react';
import { Button } from '@uhg-abyss/web/ui/Button';
import { Layout } from '@uhg-abyss/web/ui/Layout';
export const HelloAbyss = () => {
const [cartCount, setCartCount] = useState(5);
const addToCart = () => {
const newCount = cartCount + 1;
setCartCount(newCount);
};
return (
<Layout.Stack>
<Button onClick={addToCart}>Add Item</Button>
<abyss-parcel import="parcel-app@test" count={cartCount}></abyss-parcel>
</Layout.Stack>
);
};
Table of Contents