ParcelBoundary

ParcelBoundary is a React component. Its job is to optimise rendering performance, and to optionally control the flow of parcel changes.

Each ParcelBoundary is passed a Parcel. By default the ParcelBoundary uses pure rendering, and will only update when the Parcel’s data changes to avoid unnecessary re-rendering.

ParcelBoundaries have an internal action buffer that can hold onto changes as they exit the boundary. These are normally released immediately, but also allow for debouncing changes, or putting a hold on all changes so they can be released later.

import ParcelBoundary from 'react-dataparcels/ParcelBoundary';
<ParcelBoundary
    parcel={Parcel}
    debounce={?number}
    pure={?boolean}
    forceUpdate={?Array<*>}
    hold={?boolean}
    debugBuffer={?boolean}
    debugParcel={?boolean}
>
    {(parcel, actions, buffered) => Node}
</ParcelBoundary>
ParcelBoundary is also available as a React higher order component, ParcelBoundaryHoc.

Children

childRenderer

(parcel: Parcel, actions: ParcelBoundaryActions, buffered: boolean) => Node
ParcelBoundaryActions = {
    release: () => void,
    cancel: () => void
}

ParcelBoundaries must be given a childRenderer function as children. This is called whenever the ParcelBoundary updates.

It is passed a parcel, a set of actions, and a buffered boolean.

  • The parcel is on the “inside” of the parcel boundary, and is able to update independently of the parcel that was passed into the ParcelBoundary.
  • The actions can be used to control the ParcelBoundary’s action buffer (see the hold example).
  • The buffered boolean indicates if the ParcelBoundary currently contains changes that it hasn’t yet released.

The return value of childRenderer will be rendered.

// personParcel is a Parcel
<ParcelBoundary parcel={personParcel}>
    {(parcel, actions) => {
        return <input type="text" {...parcel.spreadDOM} />;
    }}
</ParcelBoundary>

Props

parcel

parcel: Parcel

The parcel that the ParcelBoundary will apply to. By default the ParcelBoundary will only update when parcel's data changes.

The parcel can be accessed from inside the ParcelBoundary via the first argument of the child renderer function, as shown here.

// personParcel is a Parcel
<ParcelBoundary parcel={personParcel}>
    {(personParcel) => {
        // personParcel is now inside the ParcelBoundary
        return <input type="text" {...personParcel.spreadDOM} />;
    }}
</ParcelBoundary>

debounce

debounce?: number // optional

If set, debounce will debounce any changes that occur inside the ParcelBoundary. The number indicates the number of milliseconds to debounce.

This can be used to increase rendering performance for parcels that change value many times in rapid succession, such as text inputs.

Debouncing explained

When the parcel in the ParcelBoundary sends a change, the ParcelBoundary will catch it and prevent it from being propagated out of the boundary. The parcel on the inside of the ParcelBoundary will still update as normal.

The ParcelBoundary waits until no new changes have occured for debounce number of milliseconds. It then releases all the changes it has buffered, all together in a single change request.

Debouncing can be good for rendering performance because parcels outside the ParcelBoundary don’t needlessly update every time a small change occurs (e.g. each time the user presses a key).

// personParcel is a Parcel
<ParcelBoundary parcel={personParcel} debounce={100}>
    {(personParcel) => <input type="text" {...personParcel.spreadDOM} />}
</ParcelBoundary>

See an example of ParcelBoundary debounce

pure

pure?: boolean = true // optional

Enables pure rendering. When pure is true, ParcelBoundary will only re-render when parcel's data changes. It defaults to true.

Use forceUpdate if you would like ParcelBoundary to re-render in response to changes in other props.

See an example of ParcelBoundary pure

forceUpdate

forceUpdate?: Array<*> // optional

While a ParcelBoundary is using pure rendering, forceUpdate will force the ParcelBoundary to re-render in response to changes in other props. Each item in the forceUpdate array is compared using strict equality against its previous values, and if any are not strictly equal, the ParcelBoundary will re-render.

// personParcel is a Parcel
// options is an array of options that are loaded after mount

<ParcelBoundary parcel={personParcel} forceUpdate={[options]}>
    {(personParcel) => <Select {...personParcel.spreadDOM} options={options} />}
</ParcelBoundary>
Example

hold

hold?: boolean = false // optional

When hold is true, all changes made to the parcel inside the ParcelBoundary are prevented from being propagated out of the boundary. The inner parcel will continue to update as normal. You can then call actions.release() to release all the buffered changes at once, or actions.cancel() to cancel all the buffered changes. This can be useful for building UIs that have a submit action.

// personParcel is a Parcel
<ParcelBoundary parcel={personParcel}>
    {(personParcel, {release, cancel}) => {
        // personParcel is now inside the ParcelBoundary
        return <div>
            <input type="text" {...personParcel.spreadDOM} />
            <button onClick={() => release()}>Submit</button>
            <button onClick={() => cancel()}>Cancel</button>
        </div>;
    }}
</ParcelBoundary>
Example

keepState

keepState?: boolean = false // optional

The default behaviour of ParcelBoundary is to update its contents whenever the Parcel it receives via props has changed.

When keepState is true, it ensures that any changes that originate from inside the ParcelBoundary are not overwritten by the incoming new props containing the updated value. This won’t be a problem for most ParcelBoundaries, but it can be if modify methods are being used above the ParcelBoundary, and if those modify methods subtly change the value that ends up being passed back down into the ParcelBoundary. In this situation you may see inputs change more than what was typed into the input.

The keepState option effectively makes the ParcelBoundary the master of its own state, and not the Parcel it receives via props. The one exception is if the change originates from outside the ParcelBoundary, in which case the ParcelBoundary will update its contents like normal.

let numberParcel = parcel
    .modifyDown(number => `${number}`)
    // ^ turn value into a string on the way down
    .modifyUp(string => Number(string.replace(/[^0-9]/g, "")));
    // ^ turn value back into a number on the way up

return <ParcelBoundary parcel={numberParcel} keepState>
    {(parcel) => <input type="text" {...parcel.spreadDOM()} />}
</ParcelBoundary>;

// without keepState, if you type "0.10" in the input above it would
// immediately be replaced with "0.1", as the new value is turned
// into a number on the way up, and into a string on the way down,
// which would make typing very frustrating.
// keepState keeps "0.10" in the text field.
Example

debugBuffer

debugBuffer?: boolean = false // optional

Wehn debugBuffer is true, ParcelBoundary will log out changes relating to its internal action buffer.

debugParcel

debugParcel?: boolean = false // optional

Wehn debugParcel is true, ParcelBoundary will log out changes to its current parcel state.