"Art prior" to React:
Martin Fowler: InversionOfControl (2005-Jun-26)
Inversion of Control is a key part of what makes a framework different to a library. A library is essentially a set of functions that you can call, these days usually organized into classes. Each call does some work and returns control to the client.
A framework embodies some abstract design, with more behavior built in. In order to use it you need to insert your behavior into various places in the framework either by subclassing or by plugging in your own classes. The framework's code then calls your code at these points.
The litmus test:
- If your code calls it, it's a library.
- If it calls your code, it's a framework.
Looking at the React tutorial Starter Code ReactDOM.render()
is the only part of React that your code calls. The rest, i.e. the majority of your code, is called by React.
Ipso facto - React is a framework because it is in control when most of your code runs.
A JavaScript library for building user interfaces
Calling itself a library is diluting (and undermining) the meaning of established software engineering terminology. At best it's evidence of ignorance of prior art, at worst it's propaganda (i.e. marketing).
At the time AngularJS already had established itself as a client side application framework which appealed to the enterprise market with its propensity to favour (complex) "batteries included" products and tools. By calling itself a library React excused itself from being directly compared with AngularJS based on feature set while at the same time presenting itself as a lighter weight solution with an API that can be "learned in a weekend".
More accurately if one accepts that AngularJS is an application framework, React can be described as a view component framework. This reflects a difference in design philosophy. Both were originally designed to operate within the constraints of a single thread within the web browser.
An application framework which takes control of that single main thread takes on the responsibility to support the requirements of the entire client-side application. AngularJS was first released in 2010. JavaScript always had async callbacks but a lot of the AngularJS developer API had a distinct synchronous feel about it (previously much of Google's Closure Library felt like it was engineered to feel more familiar to a (synchronous) Java developer). It was only in 2011 that jQuery 1.5 introduced deferred objects. By the time React released in 2013 browsers were getting pretty close to supporting promises natively, making asynchronous programming (and design) just a bit more ergonomic.
Given this shift a view component framework prioritizes the runtime needs of the view logic over the needs of the application logic. That single thread is the UI thread ("don't block the main thread"). So the client-side "application logic" only gets a chance to process anything when it is given an opportunity by the view component framework (or indirectly by the browser via asynchronous processing).
Neither the application or view component framework approach is inherently better, they are just different. There are types of use cases and applications where one is a better fit than the other and there are operating environments that can shift the balance in either direction.
However presently the demands on that main thread have come to a point where something has to give (evocative of "The Free Lunch Is Over"):
- Alex Russell - The Mobile Web: MIA
- Surma - The main thread is overworked & underpaid
- Surma - When should you be using Web Workers?
- Surma - React + Redux + Comlink = Off-main-thread
- Charlie Owen - All Constraints Are Beautiful
- Jeremy Wagner - Why Performance Matters
- Jake Archibald & Surma - Setting up a static render in 30 minutes
As to "ignorance of prior art". Just some examples:
- Nato Software Engineering Conference 1968. As referenced in
- Lambda Calculus was introduced by Alonzo Church in the 1930s.
Point being, while software engineering is relatively immature compared to other earlier established engineering disciplines there already is a lot of knowledge (and terminology, likely more than a single person can know) that is relevant to this day that predates the existence of React (2013), JavaScript (1995) and the World Wide Web (1989).
Addendum in response to https://medium.com/@attilavago/i-read-the-full-post-1340a3f338a9
though I feel it should not just be based on
ReactDOM.render()
.
That isn't the core argument. Fowler references the 1988 Johnson & Foote paper:
One important characteristic of a framework is that the methods defined by the user to tailor the framework will often be called from within the framework itself, rather than from the user's application code. The framework often plays the role of the main program in coordinating and sequencing application activity. This inversion of control gives frameworks the power to serve as extensible skeletons. The methods supplied by the user tailor the generic algorithms defined in the framework for a particular application.
ReactDOM.render()
is simply the entry point so React can "play the role of the main program in coordinating and sequencing rendering activity" and behave as a framework.
Again Fowler stated:
you need to insert your behavior into various places in the framework either by subclassing or by plugging in your own classes. The framework's code then calls your code at these points.
The starter code even demonstrates this - the Game
component extends the framework class React.Component
.
- it's React which calls
Game
's constructor to create an instance of the component. - it's React which calls the
render
method (or any of the lifecycle methods) render
returns framework specific types - react elements (somewhat obfuscated by JSX).- Apart from browser supported async processing your components cannot make a move without React.
All of the above are characteristics of a framework.
One could argue that jQuery is consuming my code at some level too, yet we still consider it a library.
jQuery for the most part is used as a layer between your code and the browser. Your code calls jQuery to fullfil some objective - and when it's done, it's done. jQuery doesn't do anything until your code calls it the next time. Some jQuery code may run due to being referenced in an event handler or because of an async callback but that is just in the nature of browser operations. jQuery as such isn't in control, your code is. jQuery is called a library because it's simply a collection of useful helpers; after you call each method your code is firmly back in charge.
With ReactDOM.render()
your code hands over control to React, past that point React is in full control and your code only runs when React decides to run it. React isn't a layer that your code calls to deal with the nitty-gritty of the browser but an environment that isolates your components from the browser and uses your components when it deems it necessary - which is exactly what a framework does.
Now maybe because callbacks are omnipresent in JavaScript, the "Hollywood Principle" becomes more difficult to spot:
"Don't call us, we'll call you" Relevant when you are writing a class/component that must fit into the constraints of an existing framework. You implement the interfaces, you get registered. You get called when the time is right. This requires a distinctly different way of thinking from that which is taught in introductory programming where the student dictates the flow of control.
- React calls your components when it needs to create React elements and it manages component state outside of your component.
- Your code calls jQuery methods to fiddle with the browser's DOM.
but I feel that React essentially still does the same as jQuery, at a different level — the VDOM.
It's the inversion of control (IoC) that makes all the difference. Granted React isn't a full fledged application framework the way Angular is but the way it uses IoC makes React a framework in the general sense. If you want to call it a VDOM framework, fine.
And exactly because React takes almost full control of the environment it has to provide the sort of infrastructure you typically see in application frameworks to make it possible to build applications. i.e. Context and Hooks.
Then there are those patterns that hint at building full fledged applications by splicing "the rest" into or in between the visual components:
- Lifting State Up vs Redux
- Container Components (i.e. non-visual)
- Sneaking in application functionality via Higher-Order Components and the Provider Pattern.
So both jQuery and React stand between your code and the browser('s DOM) but your code's pattern of interaction with them is very different.
- jQuery is a collection of helpers that your code calls whenever it needs to.
- React on the other hand calls your code - "The components supplied by the developer tailor the generic rendering algorithms defined in React for a particular application" (paraphrasing "The methods supplied by the user tailor the generic algorithms defined in the framework for a particular application." ; from Designing Reusable Classes).
Update 2021-07-23
The flawed logic behind "React is a library because it isn't an application framework":
- Library vs Framework
- Application frameworks are frameworks
- React isn't an application framework because it isn't a "batteries included" solution
- Conclusion: React is a library
Compare this to:
- Reptile vs Mammal
- All species of whales are mammals
- A mouse is very different from any species of whale. It is much smaller than any whale and lives on the land while whales live in the water.
- Conclusion: A mouse is a reptile
See the problem?
Jack Franklin – Abstractions, complexities and off-ramps – All Day Hey! 2023