Data Flow

To prevent code complexity and unexpected side effects, data should flow in one direction, from parent to child.

When a component decorates a field with @api to expose it as a public property, it should set the value only when it initializes the field. After the field is initialized, only the owner component should set the value. A component should treat values passed to it as read-only.

To trigger a mutation for a property value provided by an owner component, a child component can send an event to the parent. If the parent owns the data, the parent can change the property value, which propagates down to the child component via the one-way data binding.

A non-primitive value like an object or array that's passed to a component is read-only. The component cannot change the content of the object or array. If the component tries to change the content, you see an error in the browser console: Uncaught Error: Invalid mutation: Cannot set "msg" on "[object Object]". "[object Object]" is read-only.

To mutate the data, make a shallow copy of the objects you want to mutate. Let's take a look at a component that updates the value on an object.

Create a shallow copy of the object, which copies only the top-level properties. The values of the nested objects are not copied. In this example, {"msg":"hello"} is updated to {"msg":"hello!"} when the button is clicked.

You can also reassign the value of the object using this.obj = { msg: 'My new message' }. The myCmp component owns the obj field, and it can assign a new value to the field. Contrastingly, if the component attempts to change the nested value on the object with this.obj.msg = 'new value', you get an invalid mutation error in the web console.

Similarly, non-primitive values with references from a parent component are wrapped in a proxy and can't be modified. See Set Properties on Child Components.

To trigger a mutation for a property value provided by an owner component, a component should send an event to the owner.

We recommend using primitive data types for properties instead of using object data types. Slice complex data structures in a higher-level component and pass the primitive values to the component descendants.

There are a few reasons that it’s better to use primitive values.

  • Primitive values require specific @api properties that clearly define the data shape. Accepting an object or an array requires documentation to specify the shape. If an object shape changes, consumers break.
  • Standard HTML elements accept only primitive values for attributes. When a standard HTML element needs a complex shape, it uses child components. For example, a table element uses tr and td elements. Only primitive types can be defined in HTML. For example, <table data={...}> isn't a value in HTML, only in Lightning Web Components.

See Also