LWC – Setting Properties in connectedCallback()

This blog is talking about things we can do with lwc connectedCallback method. It seems to contradict itself with a certain point so I am trying to make sure.

They say that we can set property values in a connectedCallback. After that they say the following:

Do not use connectedCallback() to change the state of a component, such as loading values or setting properties. Use getters and setters instead.

This is confusing, is this some bad practice or something? I am using connectedCallback to query the server on initial load and I normally set the property in the method and it works. Just wondering if there is something I am missing here.

Best Answer

This is in fact a mistake in the LWC documentation. We are currently working on rewriting this section entirely.

The connectedCallback lifecycle hook is invoked when a component is connected to the document. This callback is invoked after all the public properties are set and can be used to set the state of the component.


As I see it, the original author was trying to warn about the fact that public properties can be updated after the connectedCallback is invoked. The connectedCallback is invoked only one time with the initial properties passed to the component. If a component derives its internal state from the properties, it's better to write this logic in a setter than in connectedCallback.

To illustrate this better, let's create a component that renders a random dog picture given its breed. We can first define a utility method to fetch the dog image.

function getPicture(breed) {
  return fetch(`https://dog.ceo/api/breed/${breed}/images/random`)
        .then(responce => responce.json())
        .then(res => res.message);
}

In the component below, the getPicture method is invoked in the connectedCallback. The imageUrl property is derived from the breed public property. Since the logic is in the connectedCallback, the imageUrl will always have the value of the initial breed value. This is not ideal for our component.

export default class DogImage extends LightningElement {
  @api breed;
  imageUrl;

  connectedCallback() {
    getPicture(this.breed).then(res => {
      this.imageUrl = res;
    });
  }
}

To solve this issue, expose breed using a getter/setter pair and move the logic there. Every time the breed public property is set, the setter is invoked and a new picture is fetched.

export default class DogImage extends LightningElement {
  imageUrl;
  _breed;

  @api
  set breed(value) {
    if (this._breed !== value) {
      this._breed = value;
      
      getPicture(value).then(res => {
        this.imageUrl = res;
      });
    }
  }
  get breed() {
    return this._breed;
  }
}

Live example: playground