[SalesForce] Another approach to child-to-parent communication: passing functions

Looking to socialize a new child-to-parent data communication mechanism my colleague has proposed that we use in our LWC project. His approach involves passing arrow function expressions from the parent component to the child, which are then called by the child when making updates.

In testing, this approach appears to perform 3-10x better than communicating with events, and is even slightly less verbose than the event-based approach that is normally recommended. Here's the rundown.

A parent component that communicates with events like this…

<template>
    <c-child name={name} onupdateevent={handleUpdateEvent}></c-child>
</template>
export default class Parent extends LightningElement {
    @track name = 'John Doe';

    // A STANDARD EVENT HANDLER
    handleUpdateEvent(e) { 
        this.name = e.detail;
    }
}

…instead passes an arrow function as an attribute to the child:

<template>
    <c-child name={name} update-function={updateFunction}></c-child>
</template>
export default class Parent extends LightningElement {
    @track name = 'John Doe';

    // ARROW FUNCTION TO PASS TO CHILD
    updateFunction = (update) => { 
        this.name = update;
    }
}

The child's template doesn't change at all:

<template>
    <lightning-input 
        type="text" 
        label="Participant Name"  
        value={name} 
        onchange={handleInputChange}
    ></lightning-input>
</template>

And in JS, instead of dispatching a custom event…

export default class Child extends LightningElement {
    @api name;

    handleInputChange(e) {
        this.dispatchEvent(new CustomEvent('updateevent', {detail: e.target.value}));
    }
}

… the child simply defines the function as a public reactive property, and calls it:

export default class Child extends LightningElement {
    @api name;
    @api updateFunction;

    handleInputChange(e) {
        this.updateFunction(e.target.value);
    }
}

This approach is made possible by a special trait of arrow functions where this always refers to the enclosing lexical scope — in this case, the parent component.

My Playground Example includes a more complex example and a speed test button. Very interested in whether the experts here can find fault with this pattern, or if it could in fact provide a suitable (dare I say preferable?) approach to component communication.

Best Answer

Firstly,
this is not documented. As a thumb-rule, salesforce always insists to use ONLY what is documented. So, this may some day be made invalid by salesforce the sameway they did Prevent Creation of Function Expressions in Dynamically Created Aura Components (Critical Update). Although passing function expressions in attributes (properties) of dynamically created components seemed normal, it seems it created memory leaks and they considered it anti pattern (The same way you are passing function expressions to javascript properties in child component). So, even what you are suggesting may become invalid someday according to LWC coding practices as its not documented.

Secondly,
this way of implementing communication is very very limited to strictly child-parent relationship.

Scenario-1: it works in this scenario

<c-parent>
    <c-child>

Scenario-2: if you want to use same child or parent in another flow, you will have to implement another relationship between parent/container and container/child

<c-parent>
    <c-container>
        <c-child>

Clearly, it will lead to much more complicated code compared to events.

Regarding performance difference, its negligible. You just pass data from 1 component to another and a user will never notice any kind of delay or fastness when you choose one over the other.

Conclusion: I would suggest events is the best way to implement communication for bottom up in hierarchy considering above 2 points.

Related Topic