Skip to content

Conversation

@trusktr
Copy link
Member

@trusktr trusktr commented Dec 14, 2025

See classy-solid docs for info.

Small example:

@element
class MyEl extends Element {
  @signal a = 1
  @signal b = 2

  @memo get sum() { return this.a + this.b }

  @effect logSum() {
    console.log('sum:', this.sum)
  }
}

The Element base class will ensure that effects will be cleaned up on disconnect, and restarted on connect by calling classy-solid's stopEffects() and startEffects() functions in connectedCallback and disconnetedCallback.

BREAKING:

  • The stopEffects() method of @lume/element's Element base class is removed.
    • migration: If you were relying on stopEffects() for customized management of starting and stopping effects, instead use the Effects class from classy-solid to manage your own set of effects separately from those that the Element base class manages.
      Old code:
      // ...imports...
          
      @element
      class MyEl extends Element {
        connectedCallback() {
          super.connectedCallback()
          this.#createEffects()
        }
       
        // Example: method that starts all effects.
        #createEffects() {
          this.createEffect(() => {...}) // method from Element base class.
          this.createEffect(() => {...}) // method from Element base class.
        }
       
        // Example: custom restarting of effects for some reason.
        restartEffects() {
          this.stopEffects() // method from Element base class.
          this.#createEffects()
        }
      }
      New code:
      // ...imports...
      import {Effects} from 'classy-solid'
      
      @element
      class MyEl extends Element {
        #myEffects = new Effects()
        
        connectedCallback() {
          super.connectedCallback()
          this.#createEffects()
        }
        
        disconnectedCallback() {
          super.disconnectedCallback()
          this.#myEffects.clearEffects()
        }
       
        // Example: method that starts all effects.
        #createEffects() {
          this.#myEffects.createEffect(() => {...})
          this.#myEffects.createEffect(() => {...})
        }
       
        // Example: custom restarting of effects for some reason.
        restartEffects() {
          this.#myEffects.clearEffects() // Use the new `Effects.clearEffects()` method.
          this.#createEffects()
        }
      }
      Any effects created with @effect or this.createEffect() are managed by the Element base class.
      The new clearEffects() method from classy-solid's Effectful() mixin or Effects class deletes previous effects, and the createEffect() method will create new ones. The stopEffects() method from classy-solid still exists (that's what the method from the Element base class used to be), but it does not delete effects, and instead holds a list of all previously created effects such that the new startEffects() method can be used to start all effects after they've been stopped. The above code can also be writte like so, without re-creating all the effects:
      New code, 2nd version:
      // ...imports...
      import {Effects} from 'classy-solid'
      
      @element
      class MyEl extends Element {
        #myEffects = new Effects()
        
        constructor() {
          super()
          this.#myEffects.createEffect(() => {...})
          this.#myEffects.createEffect(() => {...})
        }
        
        connectedCallback() {
          super.connectedCallback()
          this.#myEffects.startEffects()
        }
        
        disconnectedCallback() {
          super.disconnectedCallback()
          this.#myEffects.stopEffects()
        }
       
        // Example: custom restarting of effects for some reason.
        restartEffects() {
          this.#myEffects.stopEffects()
          this.#myEffects.startEffects()
        }
      }
      Restarting effects like this is not recommended unless the component has sub-features that need to be started and stopped independently of the element being connected into the DOM.

…`, see `classy-solid` docs for info. The examples in `@lume/element`'s README are updated to use the new effect pattern. The old pattern still works.

BREAKING:

- The `stopEffects()` method has been removed.
  - migration: If you relied on that, instead import the `Effectful()`
    mixin or `Effects` class from `classy-solid`, and manage your own
    effect separately from those that the `Element` base class manages.
    F.e. `myEffects = new Effects()`, with
    `this.myEffects.createEffect(() => ...);`, and
    `this.myEffects.stopEffects()` or `this.myEffects.clearEffects()`.
Copilot AI review requested due to automatic review settings December 14, 2025 23:00
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR integrates new @memo and @effect decorators from the updated classy-solid library (v0.4.4 || v0.5.0). The main purpose is to enhance reactivity features and improve lifecycle management of effects in custom elements.

Key Changes:

  • Adds automatic effect lifecycle management in the Element base class (start on connect, stop on disconnect)
  • Introduces new-style decorator-based effects alongside old-style imperative effects
  • Refactors from @reactive decorator to @untracked decorator
  • Updates symbol naming convention from prefix (__symbol) to suffix (symbol__)

Reviewed changes

Copilot reviewed 9 out of 27 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/decorators/element.ts Switched from reactive to untracked decorator, updated symbol names, moved signalify logic after attribute interception, improved documentation
src/decorators/element.test.ts Moved signal reactivity tests from attribute.test.ts, added tests for prototype value properties and invalid usages
src/decorators/attribute.ts Updated symbol naming convention, moved kind validation earlier, added null-safe operator for signal initializer
src/decorators/attribute.test.ts Moved signal tests to element.test.ts, added invalid usage tests, added @jsonAttribute test
src/LumeElement.ts Replaced Effectful mixin with manual Effects management, added startEffects/stopEffects calls in lifecycle hooks
src/LumeElement.test.ts Added test for effect reconnection behavior, updated error expectation tests
package.json Updated classy-solid dependency to `^0.4.4
lume.config.cjs Added import map entries for @solid-primitives/* packages
dist/* Generated distribution files reflecting source changes

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants