how do I make granular signals that works for properties of a whole pure class

438 views Asked by At

I have a class that keeps input events and lets me query the state.

class Input {

  btn() { return this._btn }

  _release() { this._btn = -1 }

  _press() { this._btn = 0 }

  listen() {
    document.addEventListener('keydown', e => this._press())
    document.addEventListener('keyup', e => this._release())
  }

  update() {
   if (this._btn >= 0) this._btn++
  }
}

I use this class like this:

  let input = new Input()
  input.listen()

  function update_every_frame() {
    input.update()
  }
  function check_for_input() {
    if (input.btn() > 0) {
      console.log('here')
    }
  }

So using solid style coding I want to make a signal that changes whenever an input btn is pressed:

  let [input, setInput] = createSignal(new Input(), { equals: false })

  function update_every_frame() {
    setInput(input => { input.update(); return input })
  }


  const check_for_input = () => { return input().btn() }

Now check_for_input is a reactive property that returns the button presses. Except this property up dates every frame because I call input.update every frame.

I don't want granular signals at the data level, where I pollute the pure data class with signals. Be cause I want to make a data class and test it independent of the library.

So how do I make granular signals that works for properties of a whole pure class.

Edit

Also for example how do I create signals or memo's for an array, or a getter property on a class so the signals are updated accordingly when the class updates it's properties:

class Test {
   array: Array

   a: number = 0
   get computed(): {
      return this.a < 4 ? 1 : 0
   }

   update() {
      this.a = 10
   }
}

let [instance, setInstance] = createSignal(new Test(), { equals: false })

let arraySignal = createMemo(() => instance().array)
let computedSignal = createMemo(() =>
  instance.computed)

How does computedSignal update on this method call:

setInstance(instance => instance.update())
1

There are 1 answers

1
trusktr On

One way you can do it is like this:

import {createMutable} from 'solid-js/store'
import {createEffect} from 'solid-js'

class Foo {
  // any properties here
  whatever = 123

  constructor() {
    return createMutable(this)
  }
}

const f = new Foo

createEffect(() => console.log(f.whatever))

f.whatever++ // reactive

or to keep the class really pure:

import {createMutable} from 'solid-js/store'
import {createEffect} from 'solid-js'

class Foo {
  // any properties here
  whatever = 123
}

const f = createMutable(new Foo)

createEffect(() => console.log(f.whatever))

f.whatever++ // reactive

You said you don't want to make signal for every property, but with classy-solid you can do it in a way that still feels like writing a regular class:

import {reactive, signal} from 'classy-solid'

@reactive
class Foo {
  @signal whatever = 123
}

const f = new Foo

createEffect(() => console.log(f.whatever))

f.whatever++ // reactive