hotwired / stimulus

A modest JavaScript framework for the HTML you already have
https://stimulus.hotwired.dev/
MIT License
12.74k stars 427 forks source link

Handling shared state #233

Closed sarahdayan closed 5 years ago

sarahdayan commented 5 years ago

Hi there!

I'm currently experimenting with Stimulus and was wondering how it handles shared (or global) state. The Managing State handbook states that:

Most contemporary frameworks encourage you to keep state in JavaScript at all times [...] Stimulus takes a different approach. A Stimulus application’s state lives as attributes in the DOM; controllers themselves are largely stateless.

Yet I'm wondering, how does this work when you want to share state between controllers?

Let's say I have some piece of data that I want to share between all my controllers, so that when it changes, all controllers have access to the new value. How would this be possible, given that the Stimulus Data API only provides access to the state of the controller's element?

For now, the only possibility I see is having one controller which only purpose is to handle the data and call it from controllers with this.application.getControllerForElementAndIdentifier (as suggested here) but this seems a bit hacky and I was wondering if there was a more idiomatic way that I haven't found yet.

Thanks in advance and congrats for the nice project!

kakoni commented 5 years ago

Well one could always use nodejs core API here. I'm talking about EventEmitter Example

///events.js
import { EventEmitter } from 'events'
export const EventBus = new EventEmitter();
///controller a
import { Controller } from 'stimulus';
import { EventBus } from './events'

export default class extends Controller {
  doSomething() {
    EventBus.emit('count', 4)
  }
}
///controller b
import { Controller } from 'stimulus';
import { EventBus } from './events'

export default class extends Controller {
  connect() {
   EventBus.on('count', (count) => console.log(  `${count}` ) )
  }
}
sstephenson commented 5 years ago

Hi there, and thanks for opening the issue!

For now, our recommendation for communicating between controllers is to nest their elements hierarchically in the DOM, dispatch custom events from the inner controllers, and observe them in the outer controller with actions. You can see an example of that here: https://github.com/stimulusjs/stimulus/issues/200#issuecomment-434731830

If you are just looking to share a resource across multiple instances of the same controller class, you can use the controller module’s closure to maintain a set of connected instances, then loop over them whenever you need to notify them of a change. For example:

const controllers = new Set

const timer = setInterval(() => {
  controllers.forEach(controller => controller.tick())
}, 1000)

export default class extends Controller {
  connect() {
    controllers.add(this)
  }

  disconnect() {
    controllers.delete(this)
  }

  tick() {
    console.log("Tick!", this)
  }
}

Closing this issue for now, but please feel free to continue the discussion over on the community forum.

sarahdayan commented 5 years ago

Hi, and thanks to you both for the answers! My first idea was to use an event emitter as @kakoni suggested, but I'll look into the recommended solution @sstephenson.

jm-maniego commented 1 year ago

Redux and Stimulus from ChatGPT

image