Serialization

The onSerialize and onDeserialize store config methods can be utilized separately or together to transform the store data being saved in a snapshot or the snapshot/bootstrap data being set to a store. Though they do not have to be used together, you can picture the parity between the two methods. You can transform the shape of your store data created in the snapshot with onSerialize, which might be to get your data in a format ready to be consumed by other services and use onDeserialize to transform this data back into the format that your store recognizes.

In the example below, we will show how this technique can be used to onSerialize complex model data being used by the store into a simpler structure for the store’s snapshot, and repopulate our store models with the simple structure from the bootstrap/snapshot data with onDeserialize.

onSerialize

onSerialize provides a hook to transform the store data (via an optional return value) to be saved to an alt snapshot. If a return value is provided than it becomes the value of the store in the snapshot. For example, if the store name was MyStore and onSerialize returned {firstName: 'Cereal', lastName: 'Eyes'}, the snapshot would contain the data {...'MyStore': {'firstName': 'Cereal', 'lastName': 'Eyes'}...}. If there is no return value, the default, MyStore#getState() is used for the snapshot data.

onDeserialize

onDeserialize provides a hook to transform snapshot/bootstrap data into a form acceptable to the store for use within the application. The return value of this function becomes the state of the store. If there is no return value, the state of the store from the snapshot is used verbatim. For example, if the onDeserialize received the data {queryParams: 'val=2&val2=23'}, we might transform the data into {val: 2, val2: 23} and return it to set the store data such that myStore.val === 2 and myStore.val2 === 23. onDeserialize can be useful for converting data from an external source such as a JSON API into the format the store expects.

Example

// this is a model that our store uses to organize/manage data
class MyModel {
  constructor({x, y}) {
    this.x = x
    this.y = y
  }

  get sum() {
    return this.x + this.y
  }

  get product() {
    return this.x * this.y
  }

  get data() {
    return {
      x: this.x,
      y: this.y,
      sum: this.sum,
      product: this.product
    }
  }
}

// our store
class MyStore {
  static config = {
    onSerialize: (state) => {
      return {
        // provides product and sum data from the model getters in addition to x and y
        // this data would not be included by the default serialization (getState)
        myModel: state.myModel.data,
        // change property name in snapshot
        newValKeyName: state.anotherVal
      }
    },
    onDeserialize: (data) => {
      const modifiedData = {
        // need to take POJO and move data into the model our store expects
        myModel: new MyModel({x: data.myModel.x, y: data.myModel.y}),
        // change the property name back to what our store expects
        anotherVal: data.newValKeyName
      }
      return modifiedData
    }
  }

  constructor() {

    // our model is being used by the store
    this.myModel = new Model({x: 2, y: 3})
    // we want to change the property name of this value in the snapshot
    this.anotherVal = 5
    // we don't want to save this data in the snapshot
    this.semiPrivateVal = 10
  }

  static getMyModelData() {
    return this.getState().myModel.data
  }
}