2

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

MadeInLagny
  • 185
  • 1
  • 12

1 Answers1

1

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.

Jomer
  • 11
  • 1
  • Can you show us a little code example? Because i'm trying to use Controller with more than one host too ... – cicciosgamino Jul 22 '22 at 09:23
  • A simple example of a controller that shares a single counter across multiple hosts: https://lit.dev/playground/#gist=07e0e23c009a28f2e759f2eb19d0a9ea – YouCodeThings Jul 25 '22 at 21:15
  • The example works with one definition of component. If you had two different components you would have two instances of the reactive controller. I don’t see how to solve that as a controller instance can only have one host. – Zerzio Jan 23 '23 at 15:58