Skip to main content

Drafts

Overview

Sometimes you want to edit a copy of part of the state while leaving the original state untouched, then either commit or discard those edits in one step. A common example is a settings form: the user can make several changes locally, then click Save or Reset. draft is built for exactly that workflow.

To create a draft:

const myDraftObject = draft(originalObject)

The draft function returns an object with the following properties and methods:

data: T

Draft data object (a copy of myRootStore.preferences in this case).

originalData: T

Original data object (myRootStore.preferences).

commit(): void

Commits current draft changes to the original object.

commitByPath(path: Path): void

Partially commits current draft changes to the original object. If the path cannot be resolved in either the draft or the original object it will throw. Note that model IDs are checked to be the same when resolving the paths.

reset(): void

Resets the draft to be an exact copy of the current state of the original object.

resetByPath(path: Path): void

Partially resets current draft changes to be the same as the original object. If the path cannot be resolved in either the draft or the original object it will throw. Note that model IDs are checked to be the same when resolving the paths.

isDirty: boolean

Returns true if the draft has changed compared to the original object, false otherwise.

isDirtyByPath(path: Path): boolean

Returns true if the value at the given path of the draft has changed compared to the original object. If the path cannot be resolved in the draft it will throw. If the path cannot be resolved in the original object it will return true. Note that model IDs are checked to be the same when resolving the paths.

Example

Using the preferences example above, imagine a model in your app state like this:

@model("myApp/Preferences")
class Preferences extends Model({
username: prop<string>().withSetter(),
avatarUrl: prop<string>().withSetter(),
}) {
// just as an example, some validation code
@computed
get usernameValidationError(): string | null {
// ...
}

@computed
get avatarUrlValidationError(): string | null {
// ...
}

@computed
get hasValidationErrors() {
return !!(this.usernameValidationError || this.avatarUrlValidationError)
}
}

Now imagine a form that lets the user change those preferences, but only applies the changes when they click Save. Until then, we do not want the live app state to change.

First, create a draft copy of the preferences:

const preferencesDraft = draft(myRootStore.preferences)

You can then pass the draft data and actions separately to a form component:

<PreferencesForm
data={preferencesDraft.data}
onSave={() => preferencesDraft.commit()}
onReset={() => preferencesDraft.reset()}
showValidationErrors={preferencesDraft.isDirty}
saveDisabled={!preferencesDraft.isDirty || preferencesDraft.data.hasValidationErrors}
resetDisabled={!preferencesDraft.isDirty}
/>

Alternatively, pass the draft object itself and let the component use the draft methods internally:

<PreferencesForm draft={preferencesDraft} />