Skip to content

How our state management system used to work (as of 2021 22 school year)

Eric Pedley edited this page Sep 13, 2022 · 2 revisions

This post is inaccurate in some ways as of PR #410. We removed flux so the chain of events is a bit shorter now, and more easily traversed since there aren't any more events tied together with string ids.

I'm writing this post partly for myself to get a better high-level picture of how we're managing our application state to help with thinking about a different way. This post will also help team members navigate the project hopefully.

The components of our state management are:

  • Two global store objects, RightPaneStore and AppStore, which are subclasses of the built-in JS EventEmitter class. The purpose of these is to store the data itself and propagate changes to components that depend on the data. Components subscribe to the emitters in the same way you subscribe to handlers in normal JS, like button.on('click', function()). The equivalent for our components is something like AppStore.on('eventName', function()) running once when the component mounts.
  • The global dispatcher object, which is a Dispatcher object from the flux library. This is how components update the application state. Components send events to the dispatcher, and each of the stores is subscribed as a listener to the dispatcher. However, we add another layer of complexity by wrapping all dispatcher write calls (calling the dispatch() method) in our own components called RightPaneActions and AppStoreActions.

One of the problems with our current architecture is the levels of obfuscation you have to go through to figure out where an event is being sent. Let's look at an example: say I'm debugging a hypothetical bug in SectionTable where the add course button no longer updates the calendar view. The flow you have to go through is:

  1. Find the addCourse function call in the button component (ScheduleAddCell)
  2. Follow that to the addCourse function in AppStoreActions
  3. See the line, dispatcher.dispatch({ type: 'ADD_COURSE', newCourse });. At this point, you might click through to see the dispatcher's code, which is just these two lines in dispatcher.js:
    import { Dispatcher } from 'flux';
    export default new Dispatcher();
    
    At this point, I can see it being pretty hard for a new dev to figure out what's happening because it's basically a dead end. To go further you have to know that there is a handler for the dispatcher events somewhere.
  4. Search for the keyword 'ADD_COURSE' throughout all the source files, or AppStore.js if you're smart and noticed that the dispatcher call came from AppStore Actions.
  5. See the this.emit('addedCourseChange') and ctrl-f it targeting all the source files, because there's literally no other way of figuring out where this routes to.
  6. Find AppStore.on('addedCoursesChange', this.updateEventsInCalendar); in ScheduleCalendar
  7. Look at updateEventsInCalendar. It makes a call to AppStore.getEventsInCalendar()
  8. Switch files again and look at that function. It's one line, return this.eventtsInCalendar;, and you notice there was a typo, eventts instead of events, which wasn't caught because JS objects just let you get any property you want even if it doesn't exist.
  9. Collapse from exhaustion

It's pretty clear we need to make some changes to simplify our architecture. I'm working on this under PR #410. If you have any other ideas, post them there or under issue #334