How to tell postMessage structured clone to ignore properties?

872 views Asked by At

I'm working on a project where I need to create an object in an iframe and then send said object to the parent window.

The problem is the postMessage is failing as the object cannot be cloned (DataCloneError) as it has a function (callback) property.

What further complicates this is there is a cyclic relationship whereby button lists contain buttons and each button has a reference to its parent list.

If this was using JSON.stringify rather than structured cloning, one could override toJSON on the button and avoid sending callback and replace list with listId to avoid the cyclic reference situation. Is there an equivalent to toJSON for structured cloning which would allow ignoring callback while maintaining a cyclic relationship, or another solution?

This is the rough gist of the situation with the error reproducable:

class ButtonList {
    constructor() {
        this.buttons = [];
    }

    addButton(button) {
        if (!this.buttons.includes(button)) {
            this.buttons.push(button);
            button.setList(this);
        }
        return this;
    }
}

class Button {
    setList(list) {
        if (!list) return this;
        if (this.list !== list) {
            this.list = list;
            list.addButton(this);
        }
        return this;
    }

    setCallback(callback) {
        this.callback = callback;
        return this;
    }

    getCallback() {
        return this.callback;
    }

    runCallback() {
        if (!this.callback) return this;
        this.callback();
        return this;
    }
}

const list = new ButtonList();
const button = new Button().setList(list).setCallback(() => console.log('Hello'));
window.postMessage(list, '*');

// DataCloneError: The object could not be cloned.

The parent window does not need to know the callback but needs to know any other properties.

1

There are 1 answers

3
nicholaswmin On BEST ANSWER

Create a new object with overwritten properties with Object.assign and send that through postMessage.

const foo = {
  bar: 'bar',
  list: { bla: 'bla' },
  baz: function() {
    console.log('baz')
  }
}

const serializable = Object.assign({}, foo, { 
  list: 3,
  baz: undefined 
})

console.log(serializable)