Lit reactive controllers fail to requestUpdate in nested components

Viewed 466

I want to create a basic state management using Lit reactive controllers. The purpose is to share property values accross the application.

The issue occurs when a controller is attached to a view and to a component nested in the view. When value inside controller changes, the value in the view gets updated, but not in the nested component.

Example: state.js contains the store logic. A view access the store to create a value and show state value. Nested components also show state value.

state.js

export class StateController {

  static get properties() {
    return {
      state: { type: Object },
      host: { type: Object }
    }
  }

  constructor(host) {
    // Store a reference to the host
    this.host = host;
    this.state = {};

    // Register for lifecycle updates
    host.addController(this);
  }

  _setStoreValue(property, val) {
    this.state[property] = val;
    this.host.requestUpdate();
  }
}

component.js

import { LitElement, html } from 'lit';
import { StateController } from '../state.js';

export class TestComponent extends LitElement {

  static get properties() {
    return {
      stateCtrl: { type: Object },
      state: { type: Object },
    };
  }

  constructor() {
    super();
    this.stateCtrl = new StateController(this);
    this.state = this.stateCtrl.state
  }

  render() {
    return html` Value in component: ${this.state?.test} `;
  }
}

customElements.define('test-component', TestComponent);

view.js

import { LitElement, html } from 'lit';
import { StateController } from '../state.js';
import './test-component.js';

export class MonTodo extends LitElement {
  static get properties() {
    return {
      stateCtrl: { type: Object },
      state: { type: Object  },
    };
  }

  constructor() {
    super();
    this.stateCtrl = new StateController(this);
    this.state=this.stateCtrl.state
  }

  render() {
    return html`
      <button @click=${() => this.setValueTest()}>Set value to 3</button>
      Value in view: ${this.state?.test}
      <h3> Component 1</h3>
      <test-component></test-component>
      <h3> Component 2</h3>
      <test-component></test-component>

          `;
  }

  setValueTest() {
    this.stateCtrl._setStoreValue("test", 3)
  }
}

customElements.define('mon-todo', MonTodo);

A button click in view.js updates this.state.test in view.js but not in component.js

1 Answers

Since you create a new StateController in both MonTodo and TestComponent, they are two different instances of the StateController which only have their specific component as host.

So the StateController in MonTodo only has MonTodo as a host and only updates that and not TestComponent. You would need to share one controller with both components and call requestUpdate on both.

Related