The useParcelBuffer function is a React hook. Its job is to control the flow of Parcel changes by providing a buffer.

It receives a Parcel (via parcel) and provides a Parcel of its own (referred to as innerParcel). innerParcel initially has the same contents as parcel. Changes to innerParcel will be applied immediately like normal.

When buffering is enabled, these changes will not be propagated up through parcel, and are instead held in an internal buffer. These can be released later, either explicitly by calling control.submit(), or automatically if debounce is being used.

In most cases you'll probably want to use the useParcelForm hook instead, which combines useParcelState with useParcelBuffer.

Please refer to the “Submitting Forms” example to see how this buffering behaves

import useParcelBuffer from 'react-dataparcels/useParcelBuffer';
let [innerParcel, control] = useParcelBuffer({
    parcel: Parcel,
    // optional
    buffer?: boolean,
    debounce?: number,
    beforeChange?: ParcelUpdater|ParcelUpdater[]



parcel: Parcel

The useParcelBuffer hook must be passed a parcel, the Parcel which the buffer will apply to.

Behaviour notes

Whenever a useParcelBuffer hook receives a new Parcel via props, its default behaviour is to:

  • update to contain the new Parcel’s data
  • forget about all its buffered changes

This is safe default behaviour because changes in the buffer may not be compatible with the new Parcel’s data shape. However it may be user unfriendly in some cases, depending on when and how often the parcel updates.

If you would like to keep the changes in the buffer, and you know that buffered changes will always be compatible with any new Parcel’s data shape, consider using useParcelForm.rebase or useParcelState.rebase.


buffer?: boolean = true // optional

When buffer is true, changes that occur to innerParcel will be caught in useParcelBuffer’s buffer, until released explicitly by calling control.submit(), or automatically if debounce is being used.

When buffer is false, changes will propagate up to parcel immediately.


debounce?: number // optional

If set, debounce will debounce any changes that enter the buffer. The number indicates the number of milliseconds to debounce.

This can be used to make autosaving forms.

Debouncing explained

When the innerParcel sends a change, the useParelBuffer hook will catch it and prevent it from being propagated out of the buffer.

The useParcelBuffer hooks 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.


beforeChange?: ParcelUpdater|ParcelUpdater[] // optional

type ParcelUpdater = (value: any, changeRequest: ChangeRequest) => any
type ParcelUpdater = asNode((node: ParcelNode, changeRequest: ChangeRequest) => ParcelNode);
type ParcelUpdater = asChildNodes((nodes: any, changeRequest: ChangeRequest) => any);

The beforeChange parameter accepts either a single function, or an array of functions. Whenever a new parcel is taken into useParcelBuffer from params, and whenever the useParcelBuffer hook recieves a change from below, the change is passed through each beforeChange function.

Internally the useParcelBuffer hook uses Parcel.modifyUp() on each of the beforeChange functions. If more than one function is passed to beforeChange, the change will go through the first function in the array first, then the second etc.

This is particularly useful for setting derived data, and plugins such as validation are built to be passed into beforeChange.

Please be careful

This method is safe to use in most simple 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.

To find out why, and what to do about it, please read about parcel updaters.

// parcel.value is "ABC"

let [innerParcel] = useParcelBuffer({
    beforeChange: value => value.toLowerCase()

// ^ "ABC" will be passed through `beforeChange`
// and useParcelBuffer's Parcel will contain a value of "abc"
// parcel.value is now "abc"


// ^ "HELLO" will be passed through `beforeChange`
// and useParcelBuffer's Parcel will contain a value of "hello"
// parcel.value is now "hello"


[innerParcel: Parcel, control: ParcelHookControl]


innerParcel: Parcel

The first element of the returned array is the innerParcel, a Parcel that contains the current state of parcel with all the changes in the buffer applied to it. When buffering is enabled, any changes that innerParcel receives will go into the buffer.


control: ParcelHookControl

The second element of the returned array is a ParcelHookControl instance.


class ParcelHookControl {
    submit: Function,
    reset: Function,
    buffered: boolean,
    actions: Action[]
  • submit : () => void

    When called, this function will release all changes in the buffer.

  • reset : () => void

    When called, this function will remove all changes in the buffer, and reset the data in innerParcel to be the same as parcel.

  • buffered : boolean

    This boolean is true when there are any changes in the buffer, or false if the buffer is empty.

  • actions : Action[]

    An array of actions that are currently in the buffer.