ReactJS - From Vision to Architecture
Architectural style and patterns
ReactJS does not enforce a particular way to organize and structure the codebase of a web application. Beyond some design principles and recommended file structure, the developer is completely free in how to use React. The versatile nature allows a developer or team of developers to decide on their own architectural patterns. However, a project with no structure means that everyone can do whatever they want. Thus, developers must decide on their architectural structure to keep the project productive in the long term and to make the codebase easy to navigate, modify and scale.
Container view
In this section, we shall dive deeper into the container view, or the main execution environment used to deploy the system. ReactJS is a front-end library, not an application, so the container view of ReactJS does not include components of the application such as database, etc. From a high level, it can be seen as one user interface (UI) of an application and ReactJS. UI has been included as it is the place where the user can interact.
As can be seen from the container view diagram, the whole flow is explained below based on the article 1 :
- End-user dispatches an action via the UI of the application.
- Store calls the reducer and hands over two things, the current state and the action.
- Reducer computes the next state.
- Root reducer combines reducer output into a single state tree.
- The store saves the state returned by the root reducer. The view will get updated and the end-user can see the changed view.
Components view
React allows for the manipulation of a virtual domain object model (DOM) that interacts with the real domain object model of the browser, allowing for components that directly update when data is changed. Thus, React provides a web application user interface that changes during runtime. To break down the manipulation of a virtual DOM we will decompose React into the following components. Host tree, Host instances, React Renderer, React Elements, and React Components (see diagram).
React programs usually output a tree that may change over time and a Host tree is the representation of the UI. React helps to write a program that predictably manipulates a complex host tree in response to external events. The host tree is represented initially in the form of a tree of nodes, the “host instances”. Host instances are regular DOM nodes with their properties and can be manipulated instances using APIs e.g. appenChild and removeChild. React provides a rendered such that a developer does not have to call such APIs.2
The React renderer tree uses a diffing algorithm to determine the minimal amount of changes necessary to transform the cached tree into the new tree. Initially, a representation of the UI is rendered in the form of a host tree. This is internally cached and the tree is then flushed to the render target (the DOM) to effectuate it. Any changes to the state will result in the tree being re-rendered The resulting tree is then compared to the cached
In react, the smallest building block is a “React element”. Elements are plain JavaScript objects and are lightweight and have no host instances tied to them. Therefore, react elements are immutable. Once you create an element, you cannot change its children or attributes. The only way to update the UI is to create a new element and pass it to ReactDOM.render(). It is like a single frame in a movie: it represents the UI at a certain point. React components are the primary building blocks of React. It utilizes the react elements to design UIs. Components can accept inputs, called “props”, and return React elements based on those props. A react component is either a JavaScript class or a pure JavaScript function.3
Connectors view
Having decomposed React into components there are several connectors between these components that ensure communication and coordination. The diagram below shows how different components are interconnected. A brief description is given for each connector.
The React-DOM package provides the DOM-specific methods, usable at the top level of an application. This package is a direct connector for rendering components to the virtual DOM. 2
React Redux is used to connect React components to a store and dispatch actions. Custom hook APIs enable the interaction with the store and ensure encapsulation. This direct connector ensures communication of the flow and provides reliability and maintainability.4
React uses a batch update mechanism to update the real DOM. By sending updates in batches, it enables React to update with increased performance and guarantees coordination of the control flow.5
Development View
This section, covers the systems decomposition and the main modules and their dependencies, as embodied in the source code.
The React repository on GitHub is a monorepo, which means React and all its related projects are kept in a single repository). Its repository contains multiple separate packages so that their changes can be coordinated together, and issues live in one place. 6 7
The top-level folders are:
-
Packages: contains metadata (package.json) and the source code (src subdirectory) for all packages in the React repository. Changes related to code, are mostly executed in the src subdirectory of each package. It should be noted that package.json relies on devDependencies only, no direct dependencies). It can be assumed that package.json is to set up the development environment based on the contents of the scripts folder.
-
Scripts: contains all development npm scripts, giving access to npm run build, npm t, npm run flow, and more useful CLI commands.
-
Fixtures: contains a few small React test applications for contributors.
-
Build: is the build output of React. It appears after you build it for the first time.
Furthermore, there is no top-level directory for unit tests. The tests are in a directory called tests relative to the files that are tested. For example, a test for setInnerHTML.js is located in tests/setInnerHTML-test.js.
Lastly, the core of React is located in packages/react. The React Core includes all top-level React APIs, only the APIs necessary to define components are included, for example:
- React.createElement()
- React.Component
- React.Children
Run-Time View
React code is delivered to the browser as JavaScript and rendered as such on runtime. The following will discuss how React manipulates Javascript to perform certain behaviors during runtime.
Rendering
The React render has an “entry point” which is an API used to render a react element tree inside a host.
An example is the React Render which is called by using ReactDOM.render(reactElement, domContainer)
2
Which at runtime tells React to make the domContainer
host tree match the domContainer
, React will look at the element in ReactElement and will ask the React Dom renderer to create a host instance for it. This is done recursively for every child of the ReactElement.
Updates
When an entry point is used more than once perhaps to replace or update an element in the DOM, there are two ways to go about it. Either throw away the old host tree and use a new tree to replace it or traverse the tree and replace parts of it as needed. When another render call is made React checks the old tree if the element type of a change “matches” an element in the old tree, if so React updates the single element and reuses the old tree.
Recursion
As we know at the core react Components are functions, when a normal React app is built many different components are used and nested together. React will recursively call the component functions and apply the arguments set by the user until it eventually reaches its base element code to be rendered by the browser.
Example
Take the Example with a main <App />
Component, a <Layout />
component, and a <Content />
component.
The recursive process goes as follows:
ReactDom.render(<App />, domContainer)
- React calls App function and finds that it renders a
<Layout />
element with<Content />
inside. - React calls
Layout
and finds that it renders its children between two<div >
, Layouts child is currentlyContent
- React calls
Content
and finds it renders another<div>
with a<p>
inside and in that some text.
Key Quality Attributes
This section explains how React’s architecture realizes key quality attributes, and how potential trade-offs between them have been resolved.
Versatility is one of Reacts strengths, but also its weakness. How React’s architecture realizes key quality boils down to how developers write and structure their code. The composition of components is the key feature to realizing reusability and testability.
- If components are structured well, they can provide for reusability 8.
- If functionality is decoupled correctly through components, it can be more easily tested.
Although most cross-browser compatibility is taken care of by ReactJS, reusability also contributes to the compatibility 9. By using a browser-independent DOM system, React supports cross-browser compatibility 10.
Furthermore, ReactJS is un-opinionated, meaning ReactJS gives choices for several features such as routing library and state management library11. ReactJS lacks these built-in features and lets developers make decisions for those. Due to these reasons, ReactJS is considered lightweight. With this lightweight feature, ReactJS uses logical coding that helps to create interactive and hassle-free websites, aiming for high usability and functionality 12.
There are not many tradeoffs existing in ReactJS as obtaining one key quality attribute often results in obtaining another key quality attribute. One thing to note is that letting high flexibility for developers can lead to low maintainability of the code if the developer is not skilled enough.
API Design Principles
Overview of the React API
The react API can be seen as a layer above the normal DOM API. Instead of a developer calling basic API points like: appendChild
, removeChild
, and setAttribute
, React does this for you during runtime.
React does have its own API that can be used, one endpoint has been discussed in this paper such as ReactDOM.render()
as well as allowing the use of common browser API calls too. This is not usually called by the developer and due to the design and use of React, developers do not need to know much about the lower-level APIs.
An overview of the responsibilities of Reacts Top-Level APIs 13 are as follows:
- Components: To let you split UI into reusable independent pieces
- Creating Elements: To allow for creating UI elements though this is discouraged to be used in favor of coding in JSX.
- Transforming & Fragmenting Elements: To allow manipulating and rendering of elements without a wrapper.
- Referencing: To allow actions to be applied like function calls from an element. An example is calling a function a class component may have contained in it.
- Suspense: Allow for components to wait.
- Hooks: A new addition to React that allows for easy one line control of the React state without writing a component class.
Reflection of the React API
The React API that a general developer uses when using React can be argued to follow the following principles:
- Few interfaces principle
- Since the React API is a layer above the normal DOM API it is built on top of it reducing the number of API entry points a developer would need to develop web applications.
- Clear interface principles
- Each API entry point has a clear and well-described use case for the user.
- Maximize information hiding
- Each API entry point when called can call multiple lower-level APIs and logic that the end-user is not aware of. Only endpoints that may be deemed necessary to a developer are exposed.
- Balance performance and reusability
- React is built to be as fast and reusable as possible, the top-level endpoints like ones for creating components and one line control with hooks allow for fast and reusable code.
- Design from the client’s perspective
- React has a list of Top-Level APIs entry points and for the most part, a developer (Reacts Client) will only ever need to interact with these.
- The use of these endpoints directly is even discouraged in favor of programming in JSX to allow for easier development using HTML-like tags which to the client is a lot cleaner and easier than multiple function calls to an API.
With the following principles, the goal of React is obvious, to be as simple to the end developer as possible. That is the rationale behind their design and we believe it successfully does so as a fully-featured React application can be written completely in JSX without directly using the core React API.
References
-
J. Hannah, “Building an enterprise react application, part 1,” Lullabot. [Online]. Available: https://www.lullabot.com/articles/building-an-enterprise-react-application-part-1. [Accessed: 13-Mar-2022]. ↩︎
-
D. Abramov, “React as a UI Runtime,” Overreacted, 02-Feb-2019. [Online]. Available: https://overreacted.io/react-as-a-ui-runtime/. [Accessed: 13-Mar-2022]. ↩︎
-
“Components and Props” React. [Online]. Available: https://reactjs.org/docs/components-and-props.html. [Accessed: 13-Mar-2022] ↩︎
-
“React Redux” React. [Online]. Available: https://react-redux.js.org/. [Accessed: 13-Mar-2022] ↩︎
-
“React Virtual DOM Explained in Simple English” Medium. [Online]. Available: https://www.google.com/search?q=how+does+react+update+the+real+dom&rlz=1C1GCEA_enNL979NL980&oq=how+does+react+update+the+real+dom&aqs=chrome..69i57j33i160.3539j0j7&sourceid=chrome&ie=UTF-8 [Accessed: 13-Mar-2022] ↩︎
-
‘Codebase Overview – React’. https://reactjs.org/docs/codebase-overview.html (accessed Mar. 14, 2022). ↩︎
-
E. Churchill, ‘The React Source Code: a Beginners Walkthrough I’, Medium, May 08, 2017. https://medium.com/@ericchurchill/the-react-source-code-a-beginners-walkthrough-i-7240e86f3030 (accessed Mar. 14, 2022). ↩︎
-
A. Mittal, “React architecture patterns for your projects,” OpenReplay Blog, 22-Dec-2021. [Online]. Available: https://blog.openreplay.com/react-architecture-patterns-for-your-projects. [Accessed: 13-Mar-2022]. ↩︎
-
G. Tiwari, “Browser compatibility for reactjs web apps,” BrowserStack, 16-Nov-2021. [Online]. Available: https://www.browserstack.com/guide/browser-compatibility-for-reactjs-web-apps. [Accessed: 13-Mar-2022]. ↩︎
-
“Dom elements,” React. [Online]. Available: https://reactjs.org/docs/dom-elements.html#gatsby-focus-wrapper. [Accessed: 13-Mar-2022]. ↩︎
-
R. Jentzsch, “What does it mean when they say that react is lightweight?,” Quora, 06-Jun-2020. [Online]. Available: https://www.quora.com/What-does-it-mean-when-they-say-that-React-is-lightweight. [Accessed: 13-Mar-2022]. ↩︎
-
“REACT JS,” ExciteMarkup. [Online]. Available: https://www.excitemarkup.com/outsource-react-js-development.php. [Accessed: 13-Mar-2022]. ↩︎
-
“React Top-Level API” React. [Online]. Available: https://reactjs.org/docs/react-api.html. [Accessed: 13-Mar-2022] ↩︎