Skip to main content

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.

Note: 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