salesforce / lwc

⚡️ LWC - A Blazing Fast, Enterprise-Grade Web Components Foundation
https://lwc.dev
Other
1.63k stars 394 forks source link

Expose all @wire configurations on the element instance in non-prod modes #1902

Closed kevinv11n closed 1 year ago

kevinv11n commented 4 years ago

Is your feature request related to a problem? Please describe.

@wire'd properties are concise but don't provide insight into if and when a value is provisioned.

A customer recently shared this perspective:

I love how "wiring" Apex methods to properties reduces lines of code. But it makes debugging impossible. I have this js and see in the Apex logs that the method indeed is called but nothing ever reaches the Javascript property.

Consider this example LWC component:

import { LightningElement, wire} from 'lwc';
import myApexMethod from '@salesforce/apex/MyController.myApexMethod';

export default class MyComponent extends LightningElement {
  @wire(myApexMethod, { args })
  wiredProperty;
}
<template>
  <template if:true={this.wiredPropery.data}>
    {this.wiredPropery.data}
  </template>
</template> 

If this.wiredProperty.data isn't rendered, what was the cause?

Describe the solution you'd like

When in non-production mode, provide a developer with insight into:

  1. When a component's @wires provision a value (data or error)
  2. The resolved configuration that is responsible for a provisioned value
  3. When changes to resolved configuration are observed

This can be done by exposing the values on the element instance keyed from a known symbol. Continuing the example above, imagine running this code in the dev console after selecting the c-my-component element:

> $0[Symbol.for('@wire')]
{ 
  wiredProperty: {
    data: { ... }, // provisioned data, if any
    error: { ... }, // provisioned error, if any
    config: { ... } // resolved config values
  }
}

With wire reform getting the resolved configuration values is simple.

Describe alternatives you've considered

#1. Lifecycle Hook

Comparable to connectedCallback(), if you implement a (static?) function it is invoked when wires update: config, provision of value/error.

export default class MyComponent extends LightningElement {
  **wiredCallback**({wireName, config, value}) {
  }
}

This approach was rejected because a single codebase is used for dev and prod mode, which means this debugging logic contaminates the codebase.

#2 Metaconfig for a wire

Add an additional parameter to @wire which is a meta-config object.

export default class MyComponent extends LightningElement {
  @wire(getRecord, { ... }, **{ verbose: true }**)
  wiredProperty;
}

This approach was rejected because it restricts future evolution of @wire. It also suffers from placing the toggle for debug mode into the code whereas it is configured external to the component source code (eg on Lightning Platform it's via the Setup menu, in OSS it's a compiler/bundler flag).

#3 Callback on @wire

Add a third parameter arrow function to the @wire declaration that is invoked only in dev mode.

export default class MyComponent extends LightningElement {
  @wire(getRecord, {...config }, **(_debugOpts) => {...}**)
  wiredProperty;
}

This approach was rejected because it restricts future evolution of @wire. It also suffers from placing the toggle for debug mode into the code whereas it is configured external to the component source code (eg on Lightning Platform it's via the Setup menu, in OSS it's a compiler/bundler flag).

pmdartus commented 4 years ago

Thanks for raising this issue @kevinv11n.

The main concern that I have with a symbol attached to the host element is that it leaks internal details of the component on the host element. The wired properties have nothing to do with the custom element but with the component (those are 2 distinct objects in LWC). As a side effect, the parent component might also be able to observe in dev mode the child component wired properties, but this is less of an issue.

// From c-my-parent
this.querySelector('c-my-component')[Symbol.for('@wire')]

That being said, I think we should start exposing more information via the devtool to help developers debug LWC components and wired properties/methods. A preferable approach IMHO would be to define new APIs that a dedicated browser extension to hook into to observe and report the component state.

caridy commented 4 years ago

Good stuff! I'm very sympathetic with this. As for @pmdartus' concerns, I think we can mitigate that by not using Symbol.for, and instead, install one symbol per wired field, e.g.:

image

As you can see, by creating a new symbol every time, per instance, per wire, it reads very nice, and it is very hard for anyone to attempt to use this information before they realized it is a dev-mode only thing.

caridy commented 4 years ago

Btw, I believe this is an old issue for this somewhere. This was @philcalvin's first complain about LWC a long time ago... let's get this solved.

khawkins commented 2 years ago

The proposed facility is arguably a central component to the LWC development experience. @wires are an area where any number of things can go wrong, and with varying consistency. Having access to more detailed error feedback early in the development cycle should both improve the quality of delivered components, and shorten the time to market for LWC developers.

kevinv11n commented 1 year ago

Fixed in #3090. Thanks, @jodarove ! 🎉