ParcelBoundaryHoc

ParcelBoundaryHoc is a React higher order component. Its job is to control the flow of parcel changes. It is the higher order component version of a ParcelBoundary.

Each ParcelBoundaryHoc is given a name, and expects that it will be given Parcel as a prop of the same name.

ParcelBoundaryHocs 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 or cancelled.

Unlike ParcelBoundary, it cannot use pure rendering.

import ParcelBoundaryHoc from 'react-dataparcels/ParcelBoundaryHoc';
ParcelBoundaryHoc({
    name: string | (props: *) => string,
    debounce?: number | (props: *) => number,
    hold?: boolean | (props: *) => boolean,
    modifyBeforeUpdate: Array<Function>,
    onCancel?: Array<Function>,
    onRelease?: Array<Function>,
    // debugging options
    debugBuffer?: boolean,
    debugParcel?: boolean
});
ParcelBoundaryHoc is also available as a React component, ParcelBoundary.

Config

name

name: string | (props: *) => string

The name of the prop that will contain a parcel. This should correspond with a prop containing a Parcel.

The parcel is allowed to be undefined, in which case the ParcelBoundaryHoc will have no effect.

debounce

debounce?: number | (props: *) => number // optional

If set, debounce will debounce any changes that occur inside the ParcelBoundaryHoc. 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 ParcelBoundaryHoc sends a change, the ParcelBoundaryHoc will catch it and prevent it from being propagated out of the boundary. The parcel on the inside of the ParcelBoundaryHoc will still update as normal.

The ParcelBoundaryHoc 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 ParcelBoundaryHoc don’t needlessly update every time a small change occurs (e.g. each time the user presses a key).

hold

hold?: boolean | (props: *) => boolean // optional

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

modifyBeforeUpdate

modifyBeforeUpdate?: Array<Updater>

// updating value - only to be used if shape doesn't change
type Updater = (value: any, changeRequest: ChangeRequest) => any;

// updating shape, including meta
type Updater = shape((parcelShape: ParcelShape, changeRequest: ChangeRequest) => any);

The modifyBeforeUpdate config option allows derived data to be set on the Parcel in the ParcelBoundaryHoc. Whenever the data in a ParcelBoundaryHoc is about to be initialised or updated in any way, it is passed through all modifyBeforeUpdate functions.

Each function in the modifyBeforeUpdate array operates just like the updater provided to Parcel.modifyUp().

Please be careful

This method is safe to use without shape() in most cases, but in some cases it should not be used:

  • If the updater gives you a primitive value or childless value, you can return anything.
  • If the updater gives you a value that has children, you can always return a primitive value or childless value.
  • If the updater gives you a value that has children, you can return a value with children only if the shape hasn’t changed.

Please ensure you do not change the shape of the value, as changing the data shape or moving children within the data shape can cause dataparcels to misplace its keying and meta information! Dataparcels stores data against each part of a deep value’s data structure, so it can only let you change the value if you promise not to alter the shape.

// example updaters
string => string + "!" // good
string => [string] // good
date => date.toString() // good
array => array.join(".") // good
array => array.map(number => number + 1) // good, shape is still the same

array => array.slice(0,2) // bad, shape has changed if array is longer that 2!
array => array.reverse() // bad, shape has changed because items have moved around!

If you need to update the shape of the data, you can do so using dataparcels/shape. The shape function will wrap your Parcel’s data in a ParcelShape which allows for safe shape editing. See ParcelShape for more details.

import shape from 'dataparcels/shape';

let parcel = new Parcel({
    value: [1,2,3]
});

let modifiedParcel = parcel.modifyDown(shape(parcelShape => parcelShape
    .push("foo")
    .push("bar")
    .setMeta({
        cool: true
    })
));

onCancel

onCancel?: Array<ContinueChainFunction> // optional

type ContinueChainFunction = (continueCancel: Function) => void

The onCancel function array can be used to add behaviour before or after a ParcelBoundaryHoc cancels the changes in its buffer.

If onCancel is provided and ParcelBoundaryControl.cancel() is called, the buffer’s changes are not immediately cancelled, and the first element of onCancel is called instead. The onCancel function is passed a continueCancel() function as an argument, and if this continueCancel() function is called then it will call the next element of the onCancel array. If continueCancel() is called on the last element in the onCancel array, then it will cancel the changes in the buffer.

ParcelBoundaryHoc({
    name: 'exampleParcel',
    onCancel: [
        // add a delay to the cancel action
        continueCancel => setTimeout(continueCancel, 1000)
    ]
});

onRelease

onRelease?: Array<ContinueChainFunction> // optional

type ContinueChainFunction = (continueRelease: Function) => void

The onRelease function array can be used to add behaviour before or after a ParcelBoundaryHoc releases the changes in its buffer.

If onRelease is provided and ParcelBoundaryControl.release() is called, the buffer’s changes are not immediately released, and the first element of onRelease is called instead. The onRelease function is passed a continueRelease() function as an argument, and if this continueRelease() function is called then it will call the next element of the onRelease array. If continueRelease() is called on the last element in the onRelease array, then it will release the changes in the buffer.

ParcelBoundaryHoc({
    name: 'exampleParcel',
    onRelease: [
        // add a delay to the release action
        continueRelease => setTimeout(continueRelease, 1000)
    ]
});

debugBuffer

debugBuffer?: boolean = false // optional

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

debugParcel

debugParcel?: boolean = false // optional

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

Child props

${name}

${name}: Parcel

ParcelBoundaryHoc’s child component will receive a Parcel as a prop, with the name of the prop specified by config.name. This parcel is on the “inside” of the parcel boundary, and is able to update independently of the parcel that was passed into the ParcelBoundaryHoc.

If ParcelBoundaryHoc doesn’t receive a parcel as a prop at the name indicated by config.name, then this child prop will not exist.

${name}Control

${name}Control: ParcelBoundaryControl

type ParcelBoundaryControl = {
    release: () => void,
    cancel: () => void,
    buffered: boolean,
    buffer: Action[],
    originalParcel: Parcel
}

ParcelBoundaryHoc’s child component will receive a ParcelBoundaryControl, which can be used to control the ParcelBoundary’s action buffer, and information about the current state of the action buffer.

ParcelBoundaryControl

  • The release() function will release any changes in the buffer, allowing them to propagate upward out of the ParcelBoundary.
  • The cancel() function will cancel any changes in the buffer.
  • The buffered boolean indicates if the ParcelBoundary currently contains changes that it hasn’t yet released.
  • The buffer array contains the actions that are currently held in the buffer.
  • originalParcel contains the Parcel that was passed into the ParcelBoundaryHoc, unaffected by any buffering or ParcelBoundaryHoc state.

If ParcelBoundaryHoc doesn’t receive a parcel as a prop at the name indicated by config.name, then this child prop will not exist.