I would say the traditional distinction between a library and a framework, to the extent that the industry has any settled definition for these terms at all, is about who dictates the application architecture and the control flow.

A framework typically establishes a runtime environment and then calls into your code to respond to specific events. Common examples are GUI applications that run an event loop responding to operating system events and web servers where the entry points to your own code are handlers for different requests. In the early days, JavaScript was mostly used similarly, as the entry point to your own JS code tended to be a specific event on a specific DOM node. There might be some code to set things up when your program starts, but once you hand over control to the framework, it decides what the run and when, and your code doesn’t do anything unless the framework asks it to.

A library, in the not-a-framework sense, works the other way around. Your own code ultimately has control and you call into the library when you require specific functionality. The library doesn’t do anything until you ask it to.

So to answer your question, yes, I did know those things, but I don’t think the equivalence between those calls is necessarily appropriate in this context. It’s not just about who calls the function the first time. It’s also about what happens next — whether your own code continues to call the library in a similar way later, or whether you hand over control to a framework once and that deals with everything from then on. Calling ReactDOM.render doesn’t have to be a fire-and-forget operation; it can simply perform a single render and then return having completed its task. You might then call it many further times to rerender that part of your page in response to changes in the data it depends on.

Control will often flow back and forth between your own code and library or framework code. Sometimes this happens on a small scale, like the sorting example I mentioned where you are calling a higher order function and it takes a simple callback function as a parameter. Sometimes it happens on a much larger scale, like calling React to render a component you supply, and then React calling that component’s own render logic, which in turn calls back into React to create child components, and then React calling their own render logic, and so on.

You can certainly argue that the latter starts to resemble a framework, but if you’re only talking about rendering a component tree that is triggered by a call from your own code and returns control to your code again once the rendering has finished, the difference is merely a matter of scale. Surely everyone would agree that the sorting example is a library call, so where do you draw the line? The overall architecture of your application and its control flow is still determined by your own code.

You certainly can use React more like a framework, in the sense that you call into it once to set things up and then hand over control. You can write components that have additional responsibilities like maintaining your application state and fetching data from remote APIs, and you can use hooks (or class-based component state and lifecycle methods) so that interesting events from those other sources will automatically trigger a rerender.

My point is that you don’t necessarily have to design your application in that style just because you’re using React. If you choose not to, then React doesn’t “call you”, it doesn’t permeate your codebase in the way you described before, and it certainly doesn’t exclude the use of architectural patterns like MVC or MVVM.