Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handling shared state #233

Closed
sarahdayan opened this issue Mar 23, 2019 · 3 comments

Comments

Projects
None yet
3 participants
@sarahdayan
Copy link

commented Mar 23, 2019

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

This comment has been minimized.

Copy link

commented Mar 28, 2019

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

This comment has been minimized.

Copy link
Contributor

commented Mar 28, 2019

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: #200 (comment)

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

This comment has been minimized.

Copy link
Author

commented Mar 29, 2019

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.