-
Notifications
You must be signed in to change notification settings - Fork 64
How our state management system used to work (as of 2021 22 school year)
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
andAppStore
, which are subclasses of the built-in JSEventEmitter
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, likebutton.on('click', function())
. The equivalent for our components is something likeAppStore.on('eventName', function())
running once when the component mounts. - The global
dispatcher
object, which is a Dispatcher object from theflux
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 thedispatch()
method) in our own components calledRightPaneActions
andAppStoreActions
.
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:
- Find the
addCourse
function call in the button component (ScheduleAddCell
) - Follow that to the
addCourse
function inAppStoreActions
- 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 indispatcher.js
: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.import { Dispatcher } from 'flux'; export default new Dispatcher();
- Search for the keyword
'ADD_COURSE'
throughout all the source files, orAppStore.js
if you're smart and noticed that the dispatcher call came from AppStore Actions. - 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. - Find
AppStore.on('addedCoursesChange', this.updateEventsInCalendar);
inScheduleCalendar
- Look at
updateEventsInCalendar
. It makes a call toAppStore.getEventsInCalendar()
- Switch files again and look at that function. It's one line,
return this.eventtsInCalendar;
, and you notice there was a typo,eventts
instead ofevents
, which wasn't caught because JS objects just let you get any property you want even if it doesn't exist. - 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