Rambling on Software

Mostly about software, but I can ramble about anything...

The React community seems to eschew the use of 'controllers' within a React application. In fact, there is a tendency towards positioning React/Flux as an entirely new paradigm. As much as I am extrememly happy to be able to use React to build web apps, those of us old enough to have built desk-top applications are familiar with the notion of an immediate-mode UI (see blog post from James Long where he talks about retained vs immediate mode UI development). After having been involved in prototyping an application in React for a few weeks, it still feels to me as though there is a place for controllers in this universe. It all comes down to separation of concerns. React components are beautiful for building view components. They provide a convenient and reasonably easy to understand framework for getting stuff displayed on the page. But should a React component be responding to events? If not, then where in the system does that responsibility lie?

I think there are two arguments against putting that functionality in the component. The first, as sugggested above, is purely separation of concerns. The logic of handling events is really quite different from the logic of rendering. At a very abstract level, the handling of UI events always maps to a state machine. Events occur and the machine moves from state to state. Certain transitions are associated with changes to what is rendered. Whether you actually implement this explicitly as a state machine does not matter, the fact remains that it is a very different type of programming logic than is associated with rendering a view.

The second is more practical in nature. The lifetime of a React component is bound by its presence in the virtual DOM. If a component is no longer rendered into the virtual DOM, it is unmounted and loses its state. However, there are UI workflows in which the state of the UI extends beyond the lifetime of the React component involved. For example, there may be filtering options that the user has selected that you want preserved for the next time that component is actually rendered. The traditional React answer of pushing state upwards to resolve this problem strikes me as violating encapsulation. If a UI component has some state that it manages, then as someone who uses that component, I should not have to be concerned about managing its internal state. An alternative approach is suggested in Chris Bell's recent post where he suggests letting stores hold some of this UI state. But this seems odd to me as well because it means that we have some stores that deal with persistent data and some that deal with ephemeral state from the UI.

The approach that we have chosen to follow is one in which we have actual controllers in addition to React components and stores. The concern of controllers in our architecture is quite narrow. In effect, they are just the state machines that I described above. Each controller is bound to a particular type and instance of React component. The controller listens to event from this component and when needed, updates the state of its React component. The lifetime of the controller is not bound by the lifetime of its React component, however, so the controller can hold ephemeral UI state that spans component lifetimes. This seems to result in a tidy separation of concerns: React components are responsible for rendering, controllers are responsible for managing ephemeral UI state, and stores are responsible for managing the interaction with persistent data on the back-end system.

And nobody won. Its just that there isn't much of a difference any longer. One of the significant factors that differentiated a library from a framework was control flow. You called a library, it did something for you, and then gave you back a result. A framework, on the other hand, would call into your code when the framework decided it was necessary. And all the mechanism that was making that decision about when to call your code was 'hidden', making it harder to reason about your application as a whole. So there were a lot of arguments about whether frameworks were evil, etc. Fast forward to today. Javascript has become incredibly popular. One of the most interesting architectural aspects of Javascript is that it is single-threaded with the result that a great many interactions occur in the form of callbacks. So, now we have, for example, JQuery which calls itself a 'library', but many of its functions take callbacks as parameters. This means that your code is called at some point in the future over which you have no control. And that sounds oddly like what happens in a 'framework'. My conclusion: frameworks are dead, long live libraries (with callbacks).