Skip to content

@wire computed properties should be restricted to values that cannot change #3956

Open
@wjhsf

Description

@wjhsf

To enable reactivity, the @wire decorator transforms code in such a way that computed properties are re-computed whenever updates are needed. This means that variables used as property keys must have string representations that never change.

Example transformation The following snippets are excerpts, not complete code.
// input.js
const symbol = Symbol.for("key");
export default class Test extends LightningElement {
  @wire(getFoo, {
    [symbol]: "$prop1",
    string: "$prop2",
    notReactive: 'just hanging out'
  })
  wiredFoo;
}
// output.js
const symbol = Symbol.for("key");
class Test extends LightningElement {
  wiredFoo;
}
_registerDecorators(Test, {
  wire: {
    wiredFoo: {
      adapter: getFoo,
      dynamic: [symbol, "string"],
      config: function ($cmp) {
        return {
          notReactive: 'just hanging out',
          [symbol]: $cmp.prop1,
          string: $cmp.prop2
        };
      }
    }
  }
});

For reactivity to properly function, the object returned by the config method must always have the keys in the dynamic array.

In #3955, we updated the @wire validation to only permit identifiers that were declared as a const. This prevents accidentally changing the value and breaking reactivity. However, this is not a complete solution, as the string representation of an object may change (e.g. { toString: Math.random }). Additionally, the solution does not permit constants that were imported from another file, even if those constants would otherwise be acceptable to use. (A simple workaround for the import problem is to declare a new constant in the component file.) We should validate that the value for a constant is not an object; only primitives should be allowed as computed property keys.

Additionally, in the same PR we allowed primitives to be used directly as property keys, as that is perfectly valid JavaScript with a static string representation. We did not permit template literals, as they contain expressions that can change (e.g. `${Math.random()}`). We may want to do analysis of template literals, and allow them to be used if we can determine that they have a static string representation (e.g. `without expressions`, `with ${1} static expression`).

For example, the following is currently allowed, but should not be.

// every time the property is computed, it results in a different value
const toStringChanges = { toString: Math.random }
@wire(getFoo, { [toStringChanges]: "$prop" }) nonDeterministicObject;

And following examples are currently not allowed, but should be.

// imported constants should be treated the same as constants declared in the file
import { IMPORTED_CONSTANT } from "./config";
@wire(getFoo, { [IMPORTED_CONSTANT]: "$prop" }) importedConstant;

// the computed string will never change for these template literals
@wire(getFoo, [`no expressions`]: "$prop" }) noExpressions;
@wire(getFoo, [`${1} expression`]: "$prop" }) oneExpression;

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions