0% found this document useful (0 votes)
5 views

2023-ist-react

This document discusses the identification and detection of code smells in React-based web applications, proposing a catalog of twelve specific code smells and a tool named ReactSniffer for their detection. The study reveals that a significant number of code smells were found in popular GitHub projects, with varying removal rates among different smells. The findings aim to assist developers in improving the maintainability and quality of their React applications.

Uploaded by

robsonmrsp
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
5 views

2023-ist-react

This document discusses the identification and detection of code smells in React-based web applications, proposing a catalog of twelve specific code smells and a tool named ReactSniffer for their detection. The study reveals that a significant number of code smells were found in popular GitHub projects, with varying removal rates among different smells. The findings aim to assist developers in improving the maintainability and quality of their React applications.

Uploaded by

robsonmrsp
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 16

Information and Software Technology 155 (2023) 107111

Contents lists available at ScienceDirect

Information and Software Technology


journal homepage: www.elsevier.com/locate/infsof

Detecting code smells in React-based Web apps


Fabio Ferreira a,b ,∗, Marco Tulio Valente a
a
Department of Computer Science, UFMG, Brazil
b
Center of Informatics, IF Sudeste MG - Campus Barbacena, Brazil

ARTICLE INFO ABSTRACT

Keywords: Context: Facebook’s React is a widely popular JavaScript library to build rich and interactive user interfaces
JavaScript (UI). However, due to the complexity of modern Web UIs, React applications can have hundreds of components
React and source code files. Therefore, front-end developers are facing increasing challenges when designing
Anti-patterns
and modularizing React-based applications. As a result, it is natural to expect maintainability problems in
Code smells
React-based UIs due to suboptimal design decisions.
Maintainability
Software design
Objective: To help developers with these problems, we propose a catalog with twelve React-related code smells
and a prototype tool to detect the proposed smells in React-based Web apps.
Method: The smells were identified by conducting a grey literature review and by interviewing six professional
software developers. We also use the tool in the top-10 most popular GitHub projects that use React and
conducted a historical analysis to check how often developers remove the proposed smells.
Results: We detect 2,565 instances of the proposed code smells. The results show that the removal rates range
from 0.9% to 50.5%. The smell with the most significant removal rate is Large File (50.5%). The smells with the
lowest removal rates are Inheritance Instead of Composition (IIC) (0.9%), and Direct DOM Manipulation (14.7%).
Conclusion: The list of React smells proposed in this paper as well as the tool to detect them can assist
developers to improve the source code quality of React applications. While the catalog describes common
problems with React applications, our tool helps to detect them. Our historical analysis also shows the
importance of each smell from the developers’ perspective, showing how often each smell is removed.

1. Introduction modern Web UIs. Thus, developers can modularize the UI into indepen-
dent and reusable elements and reason about each one in isolation [3].
According to the StackOverflow Survey,1 JavaScript is the world’s These components can also be reused in other pages and applications.
most popular programming language for the eighth year in a row.2 However, due to the complexity of SPAs, the front-end layer of
Moreover, in the last three years, HTML and CSS emerged in second a modern application can have hundreds of components and source
place.3 Essentially, this popularity reflects the importance and preva- code files. As a result, it is natural to expect that wrong, or sub-
lence of modern Web-based systems. Besides programming and markup
optimal design decisions will eventually lead to code that is hard
languages, front-end frameworks—such as Angular, React, and Vue
to maintain, understand, modify, and test. Although traditional code
—are also relevant tools for building rich and responsive Web applica-
smells describe general problems in object-oriented design [4], we
tions, usually called Single-Page Applications (SPAs) [1,2]. Their pop-
ularity on the StackOverflow Survey4 also illustrates this importance. still lack studies investigating design problems specific to web-based
For example, both Vue and React are more popular than traditional systems implemented using JavaScript front-end frameworks. Since an
MVC-based web frameworks, such as Spring, Django, and Ruby on important part of the developers are front-end developers, this is a
Rails. topic that deserves further investigation. For example, according to the
JavaScript front-end frameworks provide abstractions – usually StackOverflow Survey, 27% of the developers classify themselves as
called components – for structuring and organizing the codebase of front-end developers. Particularly, we identify the following problems

∗ Corresponding author at: Center of Informatics, IF Sudeste MG - Campus Barbacena, Brazil.


E-mail address: [email protected] (F. Ferreira).
1
https://insights.stackoverflow.com/survey/2020#most-popular-technologies.
2
However, we acknowledge these results might change according to the ranking and data sources. For example, in the May 2022 version of TIOBE – another
popular ranking of programming languages – Python is the most popular language and JavaScript appears at the 7th position.
3
Indeed, Stack Overflow ranks programming, scripting, and markup languages together. For this reason, HTML and CSS appear in their ranking.
4
https://insights.stackoverflow.com/survey/2020#technology-web-frameworks

https://doi.org/10.1016/j.infsof.2022.107111
Received 15 April 2022; Received in revised form 18 August 2022; Accepted 16 November 2022
Available online 23 November 2022
0950-5849/© 2022 Elsevier B.V. All rights reserved.
F. Ferreira and M.T. Valente Information and Software Technology 155 (2023) 107111

in this context: (1) the lack of a code smells catalog and vocabulary that we present the results of a historical analysis and show how often
can be shared among practitioners when documenting and discussing developers remove these smells. In Section 9, we discuss and put our
possible design problems related to these frameworks; (2) the lack of findings and insights in perspective. In Section 10, we detail threats to
detection tools that can warn developers about these smells; and (3) validity. Finally, we present related work in Section 11 and conclude
the lack of empirical studies on the prevalence of these smells. in Section 12.
To fill these gaps, in this paper, we propose a catalog of React code
smells. We focus on React because it is currently the most popular 2. React in a nutshell
JavaScript front-end framework [5].5 Specifically, we ask the following
research questions: Released by Facebook in 2013, React is a JavaScript library for
RQ1: What are the most common code smells when using React? building user interfaces that does not enforce any architectural pattern
RQ2: How common are the identified React smells in open-source (e.g., MVC). Instead, the library prioritizes interoperability, i.e., it can
projects? be incorporated into a system without rewriting existing code, therefore
RQ3: How often are the identified React smells removed? allowing gradual adoption.
To answer the first RQ, we identify a list of React smells by conduct- React provides a syntax extension to JavaScript called JSX
ing a grey literature review and by interviewing six professional React (JavaScript XML) for writing UI elements. Thus, instead of separat-
developers. Then, to check whether the identified smells are common in ing technologies by using markup and logic in separate files, React
open-source systems – and therefore to answer the second RQ – we first separates concerns under loosely coupled modularization units called
implement a prototype tool, called ReactSniffer, that detects the smells components containing both logic and markup.
in JavaScript React-based applications. Using this tool, we detected A React component can have parameters, called props (short for
2,565 code smells in the top-10 most popular GitHub projects that use properties) and returns a React element via the render method rep-
React. Finally, we conducted a historical analysis to check how often resenting what should appear on the UI. React also supports different
developers remove the proposed smells. We found that Large File has kinds of components. The main ones are class and function components.
the most significant removal rate (50.5%). The smells with the lowest The key difference between class and function is that the latter is just
removal rates are Inheritance Instead of Composition (IIC) (0.9%), and a JavaScript function that accepts props as an argument and returns
Direct DOM Manipulation (14.7%). JSX. There is no render method in functional components, and they
To the best of our knowledge, and as our key contribution, we are usually do not have state. That is, a functional component is by defi-
the first to propose a list of code smells for React-based JavaScript nition a render method. For example, the following codes show the
applications. For example, previous studies identified code smells same component implemented using class component (see Listing 1)
in pure JavaScript [6], HTML [7], Cascading Style Sheets (CSS) [8], and function component (see Listing 2).
Elixir [9], and MVC frameworks [10], but did not focus on smells
specific to the usage of JavaScript front-end frameworks. Therefore, our class Comment extends React.Component {
contributions are threefold: render() {
return (
• We propose a catalog of 12 code smells for React, including five <div>
novel smells and one partially novel smell. They were proposed <div>{this.props.text}</div>
after carefully analyzing 52 documents that were collected in <div>{this.props.date}</div>
a grey literature review and by interviewing six professional
</div>
software developers. This catalog can help front-end developers
);
to better spot and fix design problems in the React components
they are responsible for. }
• We implement a prototype tool, called ReactSniffer, to detect the }
proposed code smells. This tool works as a complement to the
proposed catalog, by supporting the automatic identification of Listing 1: Example of Class Component.
the smells proposed in our work. It is also publicly available as a
NPM package.6 function Comment(props) {
• We use ReactSniffer to unveil the most common code smells in Re- return (
act-based systems. Moreover, we also show how often developers <div>
remove these smells. These studies were important to show the <div>{props.text}</div>
proposed smells indeed occur in real-world React-based projects. <div>{props.date}</div>
For example, we found smells in all systems in our dataset (10 </div>
systems, in total). To complement, we also showed how the );
removal rate varies among the smells in our catalog (since it is
}
not enough to show the number of occurrences of the proposed
smells, but also to show how frequently they are removed). Listing 2: Example of Function Component.
The remainder of this paper is organized as follows. In Section 2, React components support one-way data binding. Therefore, when
we present background information on React. In Section 3, we present data changes in the model, React updates and re-renders the compo-
the methodology we used to define our catalog of React code smells. nents in the view.
In Section 4, we present our catalog of smells. For each smell, we
provide its definition and an illustrative example. We detail our code 3. Methodology
smell detection tool, ReactSniffer, in Section 5. Section 6 presents a field
study when we use ReactSniffer in ten GitHub projects. In Section 7, Although JavaScript front-end frameworks foster reuse and modu-
larity, developers may also make design decisions that lead to code that
is hard to maintain, understand, modify, and test. However, we still
5
https://2020.stateofjs.com/en-US/technologies/front-end-frameworks/ lack studies investigating these design problems in web-based systems
6
https://www.npmjs.com/package/reactsniffer implemented using JavaScript front-end frameworks. For this reason,

2
F. Ferreira and M.T. Valente Information and Software Technology 155 (2023) 107111

Fig. 1. Overview of the grey literature methodology.

in this section, we describe the methodology we use to derive a catalog associated with a reputable organization? (c) has the author published
of React code smells. We collect the smells following two instruments: other works in the field? (d) does the author have expertise in the area?
a grey literature review (Section 3.1) and semi-structured interviews As mentioned, we initially selected 157 articles, including React’s
with professional software developers (Section 3.2). official documentation.7 Then, we sequentially removed articles that do
not include a valid URL (9 articles), that are a copy of another selected
3.1. Grey literature review article (5 articles), that are about humans reactions to chemical smells,
i.e., aromas (6 articles), that do not meet our quality assessment levels
In this initial step, our goal is to reveal bad design practices when (22 articles) or that do not discuss or answer our research question (63
implementing React applications. Since React emerged in recent years, articles). After discarding these articles, we ended up with 52 articles
there are few studies on it, and to the best of our knowledge, we for analysis (157–105 = 52), which we refer to as A1 to A52.
are the first to study code smells in React-based systems. Therefore, Data extraction: After collecting the articles, the first author of this
as usual with emerging and technological topics, grey literature is paper carefully read and analyzed each one. He used thematic analysis
recommended as a source of evidence rather than formal literature for identifying and recording patterns (or themes) related to code smells
reviews since practitioners are the key protagonists in terms of using within the selected articles [16,17]. A tabular data extraction form was
this new technology (and sharing their experiences in blogs, QA forums, used to keep track of the extracted information. In particular, each
and ebooks) [11–14]. row of this form reports an article, and each column corresponds to
Thus, we conduct a grey literature review to answer our first a candidate code smell.
research question: Data Validation: In the data validation step, the second author
RQ1: What are the most common code smells when using React? analyzed each article to validate each candidate code smell retrieved
Fig. 1 summarizes the procedure we followed for selecting the during the data extraction step. Initially, we identified 57 candidate
articles and identifying the smells, which has four main steps: (1) smells. However, in this validation step, we decided to consider only
Google search, (2) article selection, (3) data extraction, and (4) data smells cited in more than one article, resulting in 22 candidate smells.
validation. In the rest of the section, we describe each step. Next, we discussed each candidate smell and removed nine cases that
describe low-level concerns, i.e., unrelated to design. Particularly, all
Google search: To retrieve an initial list of articles, we defined a
the removed smells are detected by state-of-the-practice linter tools
search query on code smells related to React. We made two primary
(e.g., modify state directly, array index as key, no access state in set-
considerations when defining this query. First, we added the terms react
and reactjs to restrict the search to React-related articles. Furthermore,
State , props spreading, etc.). We also removed the smell Derived state
from props because it is related to another one, Props in initial state.
we added code smell, bad smell, anti-pattern and bad practice to search
We finished with 12 candidate smells, which are summarized in
for design problems related to this framework. As a result, we used the
Table 1. The complete list of the selected articles and candidate smells
following search query:
is available at https://doi.org/10.5281/zenodo.6985604.
("react" OR "reactjs") AND
3.2. Semi-structured interviews
("code smell" OR "bad smell" OR "anti-pattern" OR
"bad practice") We interviewed six professional React developers recruited through
the first author’s social networks to validate the candidate smells iden-
tified in the grey literature review. In these interviews, we asked the
Then, we executed this query in the Google Search Engine. The participants to comment on bad design practices they observed while
first result included thousands of documents. Since JavaScript front-end working with React. Our ultimate goal was to double-check whether
frameworks are a recent and emerging technology, we set 2017 as the the smells identified in the grey literature indeed occur in real software
initial search date, and we only considered articles written in English, projects.
resulting in 157 articles. The interviewed participants have from two to six years of expe-
Article selection: Since the grey literature is not peer-reviewed, rience with React. They work with industrial projects from distinct
practitioners can share their experiences without rigorous method- organizations, ranging from startups to big tech companies. The first
ological concerns. For this reason, we rely on additional assessment author conducted the interviews, taking from 27 min (minimum) to
levels to make sure the selected articles are appropriate to our pur- 43 min (maximum). We report data about each participant in Table 2.
poses and to meet minimum qualification levels. In particular, we We started the interviews by asking the following question: What are
assessed the articles using the Quality Assessment Checklist proposed the main bad practices you observed while working with React? Thus, the
by Garousi et al. [15]. We selected articles that attend at least one of
the following Authority of the Producer criteria from this checklist: (a)
7
is the publishing organization reputable? (b) is an individual author https://reactjs.org/docs/getting-started.html

3
F. Ferreira and M.T. Valente Information and Software Technology 155 (2023) 107111

Table 1
Code smells identified in the grey literature.
React Smell Description Articles #
Props in Initial State Initializing state with props A1, A3, A6, A14, A15, A16, A17, 13
A24, A25, A26, A27, A36, A41
Large Component Component with too many props, A2, A5, A7, A9, A13, A14, A18, 12
attributes, and/or lines of code A21, A27, A40, A44, A47
Inheritance Instead of Composition Using inheritance to reuse code A1, A12, A25, A26, A29, A35, 10
among components A36, A38, A40, A49
Prop Drilling Passing properties through A1, A2, A12, A19, A29, A37, 8
multiple levels of a components A45, A49
hierarchy
JSX outside the render method Implementing markup in multiple A2, A3, A7, A25, A44, A45, A51 7
methods
Direct DOM Manipulation Manipulating DOM directly A7, A20, A28, A30, A31, A52 6
Duplicated Component Code duplication among A2, A32, A40, A46, A48 5
components
Multiple Components in the same Multiple and unrelated A7, A35, A46, A48 4
file components implemented in the
same file
Too Many Props Passing too many properties to a A3, A44, A46 3
single component
Uncontrolled Component A component that does not use A1, A20, A26 3
props/state to handle form’s data
Force Update Forcing the component or page to A1, A34 2
update
Low Cohesion Component with multiple A5, A32 2
responsibilities

Table 2 State, and JSX Outside the Render Method.8 They refer to features very
Participants experience.
specific to React, including View updates, components that manipulate
Participant React Experience Industry DOM directly, components that refer to HTML elements, components’
P01 6 years E-commerce state, and render methods.
P02 4 years E-commerce
Partially Novel Smell. We classified a single smell in our catalog
P03 4 years IT services
P04 4 years IT services
as partially novel: Prop Drilling. For example, Ousterhout et al. [18]
P05 2 years Banking mention a design red flag called Pass-Through Methods, i.e., a method
P06 2 years Banking that does nothing but only pass its arguments to another method with
a similar signature. However, in our case, Prop Drilling does not relate
to methods but to components. As a second observation, Prop Drilling
resembles to some degree a Middle Man class, i.e., a class that delegates
participants had the freedom to comment on problems not previously
most of its work to other classes [4]. However, Middle Man classes by
identified in the grey literature.
definition are anemic in the terms of behavior and data, which is not
In the second part of the interview, we provide concrete examples
the case of Prop Drilling. In other words, a complex component can
of the smells identified in the grey literature that the participants did
also be used to pass through properties to its child components.
not mention. We also asked them whether they view these examples as
Traditional Smells. In the grey literature and in the interviews with
design problems. In case of a positive answer, we asked whether it is
developers, we identified six smells that are similar to traditional
common to find the smell in projects they worked on.
smells: Large File, Large Component (which can be considered a par-
As a result, we were able to validate 11 out of 12 candidate smells,
ticular case of the traditional Large Class smell), Inheritance instead
as described in Table 3. Particularly, we were not able to validate
of Composition (since the inverse relation is considered a good object-
Multiple Components in the Same File. Two developers did not view this
oriented principle [19]), Duplicated Component (which is a particular
smell as a clear problem, as expressed in the following comment:
case of Duplicated Code), and Low Cohesion. We also consider that Too
Many Props is similar to Data Class and Large Class [4]. A Data Class
Working with too many open files can also be harmful. If the components has mostly data and only setter and getter methods. A Large Class is a
are closely related, I do not see it as a problem, it is a developer class with several responsibilities. On the other hand, Too Many Props
preference. designates a component with a large number of props. Essentially, this
smell is very similar to the well-known Long Parameter List smell,
We also identified three new smells in the interviews: Large proposed by Fowler [4]. Just to clarify, in React, properties designates
Bundlers, Lack of Accessibility, and Large Files, as summarized in Table 4. the parameters passed into components.
In this case, we selected Large Files to include in the catalog. We did
not include the first two smells because they do not appear in our grey Summary: We identify a list of 12 React smells by conducting
literature review. a grey literature review and interviewing six professional React
Therefore, our catalog ended up with 12 React Smells. In Section 4, developers: Force Update, Direct DOM Manipulation, Uncontrolled
we provide examples of each one. Components, Props in Initial State, JSX Outside the Render Method,
Large File, Large Component, Low Cohesion, Prop Drilling, Too Many
3.3. Code smells classification Props, and Inheritance instead of Composition.

After selecting the final smells, we also classified them into three
major categories: 8
JSX outside the render method are usually Large Components as well.
Novel Smells. We claim our list includes five novel smells: Force Up- Indeed, 324 out of 401 JSX smells (80.7%) detected in our Field Study
date, Direct DOM Manipulation, Uncontrolled Components, Props in Initial (Section 6) — occur in Large Comments.

4
F. Ferreira and M.T. Valente Information and Software Technology 155 (2023) 107111

Table 3
Code smells validated in the interviews, with examples of participants’ comments.
React Smell Participants’ Comments
Props in Initial State When we initialize a state with props, the component practically ignores all updated values
of the props. If the props values change, the component would still render its first values.
Direct DOM Manipulation React beginners usually use plain vanilla JavaScript to direct access a HTML DOM
element, which can cause inconsistencies between React’s virtual DOM and the real DOM.
Prop Drilling If you have a grandparent component, which passes props to the child, which passes props
to the grandchild, but only the grandchild needs these props, the mistake is to deliberately
pass props down instead of using a contextAPI.
Duplicated Component It is common to find components or parts of a component duplicated, especially if you do
not have documentation such as a storybook.
Inheritance Instead of Composition Using inheritance makes it difficult to reuse components elsewhere.
JSX outside the render method It is a bad practice. Class methods with JSX should be components, also for reuse
purposes.
Too Many Props It is also common, and the problem is worse in React because every change in a prop
generates a new request to update the view.
Large Component When a component grows, it is a sign that it needs to be broken.
Force Update This is a terrible practice, I have never used force update, but in [my-company] there is
everywhere.
Low Cohesion Components doing a lot. That is what we have the most.
Uncontrolled Components It is common among inexperienced developers or when migrating to REACT to use
uncontrolled components.

Table 4
New code smells identified in the interviews.
React Smell Description
Large Bundles A JS bundler is a tool that puts JS code and all its dependencies together in one JS
file. A large JavaScript bundle contains several components, dependencies, utility
libraries and so on.
Lack of Accessibility Dynamic components transformed to HTML non-semantic elements, which cannot be
interpreted reliably by a wide variety of user agents, including assistive technologies.
Large Files A file with several components and lines of code

4. React code smells If you only want to avoid passing some props through many levels,
component composition is often a simpler solution than context.
In this section, we present our catalog of smells. For each smell,
we provide its definition and an illustrative example. A more de-
tailed presentation of each smell is available at: https://github.com/ 4.2. Duplicated component
fabiosferreira/React-Code-Smells.

4.1. Prop drilling This smell refers to almost identical components. For example, the
problem usually occurs when multiple developers extract the same
Props drilling refers to the practice of passing props through multi- UI code to different components. As an example, in the Listing 3,
ple components in order to reach a particular component that needs the Comment and Opinion have the same code.
property. Therefore, the intermediate components act only as bridges to
deliver the data to the target component. For example, consider an App function Comment(props) {
to create a Gallery that renders images and allows comments in each return (
one (see Fig. 2). First, the App renders Gallery passing the user and <div>
avatar props. Then, Gallery renders Image passing the user and <User user={props.user} />
avatar props. Next, the Image renders the Comment components, <div>{props.text} </div>
also passing the user and avatar props. Finally, Comment renders
<div>{props.date}</div>
Author, passing again the user and avatar props. In other words,
</div>
Author is the only component that really needs to use these props.
As the size of the codebase increases, Prop Drilling makes it chal-
);
lenging to figure out where the data is initialized, updated, or con- }
sumed. Since each component is usually in a separate file, in our Gallery
example (see Fig. 2), there are five different files to check for property
updates, including, Author.jsx, Comment.jsx, Image.jsx, Gallery.jsx, and function Opinion(props) {
the file that calls the Gallery component, App.jsx. Moreover, it seems return (
that current IDEs do have help on the task of tracking property ini- <div>
tialization and updates. As a second problem, Prop Drilling results <User user={props.user} />
in tightly coupled components. For example, whenever the Author <div>{props.text} </div>
component needs more props from the top, all the intermediate levels
<div>{props.date}</div>
must be updated. Alternatives to Prop Drillings include component
</div>
composition and Context API. However, at least Context API has similar
);
problems, for example, regarding the difficulty in tracking property
updates. Finally, it is worth mentioning that React documentation }
recommends using component composition: Listing 3: Example of Duplicated Component (code smell).

5
F. Ferreira and M.T. Valente Information and Software Technology 155 (2023) 107111

Fig. 2. Example of Prop Drilling (code smell).

4.3. Inheritance instead of composition


class Employee extends React.Component {
render() {
In React, developers tend to use inheritance to tackle two problems: return (<div> Name: {this.props.name} </div>);
(1) to express containment relations, particularly when a component }
does not know its possible children; (2) to express specialization rela- }
tions, when components are ‘‘special cases’’ of other components. How-
ever, as usual in object-oriented design, React developers recommend class Developer extends Employee {
using composition over inheritance whenever possible. render() {
For example, consider a component that handles Employee and return (
a special collaborator called Developer, which has a level and <div>
receives a bonus. The Employee component can share props with {super.render()}
Developer, and a possible design to show the data consists of creating <div> Level: {this.props.level} </div>
a generic view to show data common to all employees and reuse it in
<div> Bonus: {this.props.bonus} </div>
the view that handles Developer (see Listing 4).
</div>
However, inheritance usually results a tight coupling between com-
)
ponents [19]. For example, changes in the base component can affect
}
all child components. On the other hand, by using composition instead
}
of inheritance, we can reuse only the UI behavior. Moreover, the
article A40 comments about this smell: We’re fans of composition over Listing 4: Example of the Code Smell Inheritance instead of
inheritance in pretty much any programming language. It’s tempting to treat Composition.
your component’s render method as a template method.. Listing 5 shows
the Developer component using composition instead of inheritance,
which produces the same result. 4.4. JSX outside the render method
In some cases, it may be necessary to share non-UI functionality
between components, for which React documentation recommends The render method is the only method that is mandatory in a class
using separate JavaScript modules: component. It provides the JSX template for UI elements. Normally, we
assume that all JSX code is confined in the render method. Therefore,
If you want to reuse non-UI functionality between components, we the existence of JSX code in other methods indicates that the compo-
suggest extracting it into a separate JavaScript module. The components nent is assuming too many responsibilities and providing a complex UI
may import it and use that function, object, or a class, without extending element. As a result, it is more difficult to reuse the component in other
it. pages or apps. For example, in the following Gallery component (see

6
F. Ferreira and M.T. Valente Information and Software Technology 155 (2023) 107111

class Developer extends React.Component { function Comment(props) {


render() { return (
return ( <div>
<div> <div>{props.name}</div>
<Employee name="Bob"/> <img src={props.avatarUrl}/>
<div> Level: {this.props.level} </div> <div>{props.text} </div>
<div> Bonus: {this.props.bonus} </div> <div>{props.date}</div>
</div> // other props
) </div>
} );
} }
Listing 5: Example of composition instead of inheritance. Listing 7: Example of Too Many Props (code smell).

Listing 6), we have three render methods: one to render an Image function Comment(props) {
(that calls renderComment()), the method that only renders Com- return (
ments, and the main render method (that calls renderImage()). <div>
However, suppose we should handle just a Comment. In this case, it <Avatar avatar={props.avatar} />
is impossible to rely on Gallery since this component also provides <div>{props.text} </div>
other visual elements, such as the ones needed to display an Image. <div>{props.date}</div>
</div>
class Gallery extends React.Component { );
renderComment() { }
return (<div> ... </div>)
Listing 8: Example used to reduce the number of props.
}

renderImage() { 4.6. Force update


return (
<div> JavaScript frameworks rely on a data binding mechanism to keep
... the View layer updated with data automatically. Particularly, React
{this.renderComment()} supports one-way data binding, which automatically reflects model
</div>) changes in the View. For this reason, it is considered a bad practice to
} force the update of components or even reload the entire page to update
render() { some content, as recommended in React documentation: normally, you
return ( should try to avoid all uses of forceUpdate() and only read from this.props
<div> and this.state in render().
{this.renderImage()}
... 4.7. Uncontrolled components
</div>
) Forms are key elements in Web UIs. The official React documenta-
} tion recommends using controlled components to implement forms. In
such components, React fully handles the form’s data. However, devel-
}
opers sometimes implement forms using vanilla HTML, where the form
Listing 6: Example of JSX outside the Render Method (code smell). data is handled by the DOM itself, leading to so-called Uncontrolled
Components. For example, the following AddComment component (see
In summary, the presence of JSX code in multiple methods indicates Listing 9) is considered Uncontrolled, since it uses a ref to get the
a complex UI element, which might be decomposed into smaller and comment value.
reusable ones.
class AddComment extends React.Component {
// ...
4.5. Too many props
render() {
return (
Props (or properties) are arguments passed to components via HTML <form onSubmit={this.handleSubmit}>
attributes. However, it is hard to understand components with a long <label>Comment:</label>
list of props. For example, consider the Comment component used as <input type="text" ref={this.input} />
an example in React’s documentation (see Listing 7). This component
<input type="submit" value="Submit" />
has many properties, including the four properties presented in the
</form>
following code:
);
To reduce the number of props handled by Comment, we can
}
extract the props related to avatars (i.e., name and avatarUrl) to
}
a new component, called Avatar. After that, Comment just need to
reference this new component, as shown in the Listing 8: Listing 9: Example of Uncontrolled Component (code smell).

7
F. Ferreira and M.T. Valente Information and Software Technology 155 (2023) 107111

On the other hand, when developers use controlled components, all Table 5
Front-end files extensions.
data is stored in the component’s state, making it easy to validate fields
*.js, *.jsx, *.ts, *.tsx
instantly or render them conditionally.

4.8. Low cohesion 5.1. Architecture

As usual in software design, the implementation of components As described in Fig. 3, ReactSniffer architecture has two key compo-
must be cohesive and follow the Single Responsibility Principle [20]. nents: a parser for analyzing React files and a Smells Detector module
for identifying the smells.
In other words, the component’s data and behavior must be related to
The Parser is a Command-Line Interface (CLI) implemented in
make the component reusable and easy to understand.
Node, which receives as input a valid front-end file and generates
an Abstract Syntax Tree (AST) in a JSON format. We filter front-
4.9. Large files end files according to the extensions listed in Table 5. The principal
element of this parser is Babel,9 a JavaScript compiler commonly used
to convert novel JavaScript syntactic elements (e.g., ES6 syntax) into
A large file is one with several components whose implementation backward-compatible elements, therefore allowing programs to run in
requires several lines of code. Indeed, some developers advocate that older browsers. A powerful component of Babel is its parser,10 which
we should have exactly one component per file. See for example the generates an AST for JSX code.
following comment from one the interviewed developers: The Smells Detector module relies on the AST to search and inspect
React elements. It recursively traverses the AST using a preorder algo-
rithm. To better understand ReactSniffer architecture, Listing 10 shows
I got tired of trying to fix a bug on a component co-located with other
the pseudocode of the ReactSniffer tool and Listing 11 details the major
six components in a file with thousands of lines.
steps and computations performed by ReactSniffer to detect smells in
components.
4.10. Large component Next, we discuss the smells supported by our tool and which detec-
tion strategy we implemented to recognize their instances.
JSX outside the render method: we can create methods in class com-
Clean and small components improve readability and maintainabil- ponents using two different syntaxes: traditional or arrow functions.
ity. For example, React documentation provides this recommendation Thus, we rely on two types of nodes to check whether a component
for refactoring large components: implements UI features outside its render method: ClassMethod
and ClassProperty. For ClassProperty nodes, we also verify
whether they receive an ArrowFunction. The detection strategy con-
If a part of your UI is used several times, or is complex enough on its
siders the number of methods containing UI elements, which we call
own, it is a good candidate to be extracted to a separate component.
NM_JSX. If NM_JSX is higher than a given threshold, the component is
tagged as a smell.
4.11. Direct DOM manipulation Too Many Props: To detect this smell, we count the component’s
number of props, which we call N_Props. If this value is higher than
a threshold, the component is marked as a smell.
React uses its own representation of the DOM, called virtual DOM, Large Component: To detect this smell, we rely on the number of
to denote what to render. When props and state change, React updates lines of code (LOC), the number of props (N_Props), and the number
the virtual DOM and propagates the changes to the real DOM. For of methods (NM ) in the component. We then check whether at least
this reason, manipulating the DOM using vanilla JavaScript can cause one of these values is greater than the given thresholds (each metric
inconsistencies between React’s virtual DOM and the real DOM. has its own threshold).
Force Update: To detect components that force updates, we check
whether its functions include calls to forceUpdate() or reload()
4.12. Props in initial state methods.
Direct DOM Manipulation: to detect components that manipulate
the DOM directly, we check whether they include calls to any HTML
Initializing state with props makes the component to ignore props
DOM methods, such as getElementById and getElementsBy-
updates. If the props values change, the component will render its first
TagName.
values. React documentation also states about this smell:
Props in Initial State: we check whether the state is initialized with
any component props to detect this smell.
Using props to generate state in the constructor (or getInitial- Large File: To detect large files, we rely on the number of lines
State ) often leads to duplication of ‘‘source of truth’’, for example of code (LOC), the number of components (N_Components), and the
where the real data is. This is because the constructor (or getIni- number of imports (N_Imports) in a file. We then check whether at least
tialState ) is only invoked when the component is first created. one of these values is greater than the given thresholds (each metric
has its own threshold).
Inheritance instead of Composition: We use the SuperClass AST
5. ReactSniffer: Code smell detection tool node to detect inheritance relations. If a component has the Super-
Class node and it is not a default React component, it is inheriting
from another component.
We also implemented a prototype tool – called ReactSniffer – to
detect the proposed smells. In this section, we describe its architecture
(Section 5.1), thresholds selection policy (Section 5.2), and limitations 9
https://babeljs.io/
10
(Section 5.3). https://babeljs.io/docs/en/babel-parser

8
F. Ferreira and M.T. Valente Information and Software Technology 155 (2023) 107111

Fig. 3. ReactSniffer architecture.

ReactSniffer(dirname)
smells = []
FrontEndFiles = all files in dirname with .js, .jsx, .ts, and .tsx extensions
FOREACH file IN FrontEndFiles DO
AST = GenerateAST(file)
IF isReactFile(AST) THEN
components = getComponents(AST)
FOREACH component IN components DO
smells = DetectComponentSmells(component)
IF (file[LOC] > LOC_F_th OR components.length > N_Components_th
OR file[N_Imports] > N_Imports_th) THEN
smells.add(LF);
return smells

Listing 10: ReactSniffer algorithm.

DetectComponentSmells(Component)
smells = []
IF (component[LOC] > LOC_th OR component[N_Props] > N_Props_th or component[NM] > NM_th) THEN
smells.add(LC)
IF (component[N_Props] > N_props_th) THEN
smells.add(TP)
IF (component.hasNode(’SuperClass’) OR component.hasNode(’super’)) THEN
smells.add(IIC)
IF (component.hasNode(’forceUpdate’) OR (component.hasNode(’reload’)) THEN
smells.add(FU)
IF (component.hasAnyDOMManipulationNode()) THEN
smells.add(DOM)
IF (component.hasInputWithoutState()) THEN
smells.add(UC)
FOREACH method IN component[methods] DO
IF (method.hasJSXElement()) THEN
smells.add(JSX)
IF (method == constructor AND method.hasPropsInState()) THEN
smells.add(PIS)
return smells

Listing 11: Smell detection algorithm.

Uncontrolled Components: to detect uncontrolled components, we 5.2. Benchmark-based thresholds selection

check whether components have inputs that values are not binding with
As described, most heuristics used by ReactSniffer rely on thresh-
a state. olds. Therefore, to define these thresholds, we propose the usage of a

9
F. Ferreira and M.T. Valente Information and Software Technology 155 (2023) 107111

benchmark-based approach [21,22], when the thresholds are derived Table 6


Dataset (FF: number of front-end files; Comp: number of components).
from a dataset of systems. In this case, we need a dataset of systems
Project Stars FF Comp.
(more details in Section 6.1). Next, we first must compute the respective
metrics for all systems and then use the 90th percentile value of each grafana/grafana 47,629 914 1116
apache/superset 45,230 387 438
metric as a threshold. For example, the same threshold is used by
prometheus/prometheus 41,650 34 46
Alves et al. [21] to characterize very-high risk code. On the other RocketChat/Rocket.Chat 31,970 532 815
hand, Aniche et al. [10] use a threshold of 75% (third quartile) in the ant-design/ant-design-pro 31,661 19 20
context of traditional MVC frameworks. However, since this is the first carbon-app/carbon 29,936 62 103
mastodon/mastodon 29,746 203 222
study on React-based smells, we decided to be more conservative in our
joplin/joplin 28,799 52 52
thresholds selection policy. metabase/metabase 27,881 1159 1344
getredash/redash 20,736 295 352

5.3. Limitations Total – 3,657 4,508

Currently, ReactSniffer does not detect the following code smells: Table 7
Low Cohesion, Duplicated Component, and Prop Drilling. The reason is Thresholds selection.
that these smells require a more complex implementation or metric React smell Metric Threshold
selection. For example, usually, there is a lack of consensus on recom- JSX outside the render method NM_JSX 2
mended cohesion metrics [23,24]. Regarding Duplicated Component and Too many props N_Props 13
LOC 116
Prop Drilling, their detection depends on a more sophisticated static Large Component N_Props 13
analysis. We plan to support these three smells in future versions of NM 2
our tool. LOC 225
Large File N_Components 2
N_Imports 19
5.4. Availability

ReactSniffer is publicly available on GitHub and can be easily in-


stalled via the NPM package manager. More details about the tool and
"dependencies": {
installation are available at
"react": "17.0.1",
https://github.com/fabiosferreira/reactsniffer.
"react-beautiful-dnd": "13.0.0",
"react-diff-viewer": "^3.1.1",
6. Field study
...
}
Our catalog emerged from the analysis of 52 documents from the
grey literature and was validated with six professional React devel-
opers. Despite that, we argue it is important do check whether the Next, we selected the top-15,000 most popular GitHub projects
identified smells indeed happen in the wild. Moreover, it is also im- ranked by stars, a metric commonly used to rank projects by popu-
portant to provide quantitative data about the frequency of each smell larity [25,26]. Then, we discarded 541 projects labeled as archived
described in the catalog, since it is not reasonable to assume that they by GitHub since these projects are no longer actively maintained. We
appear in the same number in real-world projects. For this reason, this also searched for forks to ensure the selected projects are not a clone
section reports the results of a field study conducted to investigate of someone else’s project. However, we did not find forks among the
whether the proposed React smells are common in open-source systems. selected projects. Finally, for the remaining 14,459 projects (15,000–
First, we formulated the following research question: 541), we checked out their package.json and bower.json files to retrieve
RQ2: How common are the proposed React smells in open-source their dependencies. Then, we selected only projects that depend on
projects? React. In total, we found 988 of such projects.
We start by creating a dataset of GitHub projects that use React From the 988 projects, we selected the top-10 projects by stars after
(Section 6.1) and by using ReactSniffer to search for smells in these manually discarding non-software projects and projects that use React
projects (Section 6.2). only in examples, tutorials, documentation, and tests.
Table 6 shows the number of stars, number of front-end files (FF),
6.1. Dataset and number of components of the selected projects. We analyzed 2,060
front-end files and 2,695 React components.
We intend to validate our catalog of smells with relevant GitHub For detecting smells, ReactSniffer relies on a benchmark-based
projects. Thus, as the first step for creating a dataset with React clients, threshold selection policy. Therefore, we computed the required thresh-
we searched for the names that identify React in two popular pack- olds before running the tool, as previously explained in Section 5.2. As
age managers: npm and bower. First, we randomly selected 10 GitHub a result, we obtained the values in Table 7, which are then used to
projects that are React clients (using GitHub’s Used-by feature). Then, obtain the results presented in the following section.
we inspected their package.json and bower.json files.11 We concluded
that both npm and bower refer to React dependencies using the string 6.2. Results
‘‘react’’, as in this file:
Table 8 details the number of instances of each smell, as we found
in our dataset. We briefly discuss the results for each one.
Inheritance Instead of Composition (IIC): We found five projects
11
We needed to perform this inspection because the Used By feature does reusing UI elements by means of inheritance instead of composition,
not allow filtering relevant projects. Moreover, the feature only works in with 20 occurrences in total. The project joplin/joplin concentrates ten
projects that explicitly enable the dependency graph computation. occurrences.

10
F. Ferreira and M.T. Valente Information and Software Technology 155 (2023) 107111

Table 8 In other words, instead of relying on React’s one-way data binding


Code smells by project (LC: Large component, TP: Too many props, IIC: Inheritance mechanism for handling this synchronization, we assume the burden
instead of Composition; PIS: props in Initial State; DOM: Direct DOM manipulation;
JSX: JSX outside the render method; FU: Force update; UC: Uncontrolled component;
to handle this task.
LF: Large File). Force Update (FU): we found seven projects that force updates and
Project LC TP IIC FU DOM JSX UC PIS LF 41 occurrences in total. A single project (grafana) concentrates 26
grafana/grafana 202 101 5 26 7 136 1 4 265
occurrences of this smell. We also manually analyzed each case. Inter-
apache/superset 129 84 0 4 5 56 1 11 152 estingly, in one case, we found a comment self-admitting a technical
prometheus/prometheus 9 5 0 0 0 5 0 0 6 debt (SATD), as shown in the Listing 13:
RocketChat/Rocket.Chat 70 61 0 0 0 0 0 0 156
ant-design/ant-design-pro 4 4 0 2 0 4 0 0 5
// Angular HACK: Since the target does not actually
carbon-app/carbon 17 7 0 0 2 2 2 0 19 ↪ change!
mastodon/mastodon 77 38 1 2 7 78 2 0 35 this.forceUpdate();
joplin/joplin 35 28 10 1 0 29 1 0 16
metabase/metabase 138 100 2 3 5 65 0 5 164 Listing 13: Example of Force Update found by the ReactSniffer Tool.
getredash/redash 45 26 2 3 0 26 0 2 44 We also discovered that grafana migrated to React after using An-
Total 726 454 20 41 26 401 8 22 867 gular since October 2017. Indeed, the project still has some parts
implemented in Angular. Therefore, the use of forceUpdate seems
to be a hack to allow a rapid migration instead of fully reimplementing
the UI according to React best practices.
JSX Outside the Render method (JSX): this is a common smell in our
dataset: nine out of ten projects have at least one occurrence, with 401 Summary: Using ReactSniffer, we detected 2,565 code smells in
occurrences in total. In relative terms, 8.89% of the components in our the top-10 most popular GitHub projects that use React. The smells
dataset have methods implementing JSX outside the render method. with the highest number of occurrences are Large Component, Too
Too many Props (TP): 10.07% of the components contain too many Many Props, JSX outside the render method, and Large File.
props, according to our thresholds. In total, we found 454 occurrences
of this smell, distributed over nine projects. The component with the
highest number of props has 131 props. Interestingly, this same compo- 7. Historical analysis
nent (DashboardContainer) has 790 LOC, seven methods with JSX
outside the render method, and inherits props from StatefulUIEle- In the first RQ, we relied on a grey literature review and on
ment, which in turn inherits from UIElement. interviews to come up with a catalog of 12 code smells for React-
Large Files (LF): all projects have at least one large file. Grafana based systems. In the second RQ, we provided quantitative data on
is the project with the highest number, both in relative and absolute the frequency of each smell. This is important, for example, to provide
terms. According to our thresholds, out of 914 React files in Grafana, guidance to developers on how often they should expect to find each
265 files (28.9%) are large. The largest file in our dataset has 1,413 smell in their projects. However, a key information is still missing in
LOC. the previous RQs, i.e., the removal rate of each smell. For example, a
Large Components (LC): all projects have large components. Specif- smell can be very common but rarely removed. Therefore, this indicates
ically, 726 (16.10%) out of 4,508 components are considered large that developers tend to see this smell as less harmful. On the other
components. The largest component has 1,089 LOC. hand, smells that are rapidly removed from the code tend to be more
Props in Initial State (PIS): We found four projects initializing their important.
state with props, with 22 occurrences in total. The project apache/su- Therefore, this section reports the results of a historical analysis
perset concentrates 11 occurrences. conducted to investigate how often developers remove the proposed
React smells in open-source systems. First, we formulated the following
Direct DOM Manipulation (DOM): we found five projects with this
research question:
smell and 26 occurrences in total. The HTML DOM methods they
RQ3: How often are the identified React smells removed?
call are getElementById, createElement, and getElement-
We start by checking out the repository of the projects in our dataset
ByClassName.
and defining three times frames of six months for our analysis: Aug-
Uncontrolled Component (UC): we found five projects with this smell
2020 to Jan-2021, Feb-2021 to Jul-2021, and Aug-2021 to Feb-2022, as
and eight occurrences in total. They usually use a ref HTML attribute
illustrated in Fig. 4. For each time frame, we downloaded the repository
to read/update form values directly from the DOM instead of using
versions at the time frame start and end dates and used ReactSniffer to
React features to handle this kind of data. The Listing 12 illustrates one
search for smells. Then, we computed the smells identified at the time
of these occurrences where a ref attribute is used to clear the value
frame start date that were removed at the time frame end date.
of a file input:
To facilitate the visualization and analysis, we group the results of
all projects by time frame. Fig. 5 shows the overall removal rates.
const clearModal = () => { The removal rates range from 0.9% to 50.5%; the smell with the
//... greatest removal rate is Large File (50.5%). In fact, all projects contain
if (fileInputRef && fileInputRef.current) { refactorings for large files. On the other hand, the Inheritance instead of
fileInputRef.current.value = ’’; Composition smell has the lowest removal rate (0.91%), and only one
} project contains a removal of Inheritance Instead of Composition (IIC).
}; However, we also have other smells with a significant removal
rate, such as Props in Initial State (26.3%), JSX outside the render
method (26.0%), and Force Update (24.2%). For example, Fig. 6 shows
<input ref = {fileInputRef} type= "file" ... />
a developer from grafana project questioning the use of Force Update
before it was refactored.
Listing 12: Example of Uncontrolled Component found by the Finally, the removal rates of the other smells are Direct DOM Ma-
ReactSniffer Tool. nipulation (14.7%), Large Component (LC) (17.4%), and Too many props
(17.5%). Four projects contain removal of Direct DOM Manipulation.
When we handle form data directly, we assume the responsibility of Fig. 7 shows a refactoring removing an occurrence of this last smell
keeping the form (UI element) and the component’s state synchronized. with a comment self-admitting a technical debt.

11
F. Ferreira and M.T. Valente Information and Software Technology 155 (2023) 107111

Fig. 4. Time frames used in the analysis.

Fig. 5. Removal rates of each smell by time frame (LC: Large component, TP: Too many props, IIC: Inheritance instead of Composition; PIS: props in Initial State; DOM: Direct
DOM manipulation; JSX: JSX outside the render method; FU: Force update; UC: Uncontrolled component; LF: Large File).

Fig. 6. Issue questioning the use of Force Update.

Fig. 7. Direct DOM Manipulation Refactoring.

Summary: The removal rates range from 0.9% to 50.5%. The smell 8. Validation with developers
with the highest removal rate is Large File (50.5%). The smells with
the lowest removal rates are Inheritance Instead of Composition (IIC) To validate ReactSniffer’s results, we recruited an experienced React
(0.9%), and Direct DOM Manipulation (14.7%). Developer to execute the tool in one of his company’s projects using the
same thresholds presented in Section 5.2. We also asked him to evaluate

12
F. Ferreira and M.T. Valente Information and Software Technology 155 (2023) 107111

Fig. 8. ReactSniffer Validation with Developers (LC: Large component, TP: Too many props, IIC: Inheritance instead of Composition; PIS: props in Initial State; DOM: Direct DOM
manipulation; JSX: JSX outside the render method; FU: Force update; UC: Uncontrolled component; LF: Large File).

Table 9 Table 10
Dataset (FF: number of front-end files; Comp: number of components). Relevance scores of ReactSniffer Evaluation.
Project FF Comp. LC TP DOM JSX LF
E-commerce project 189 181 Average 3,4 3,3 3,5 2,2 3,7

the tool results. Table 9 shows the number of front-end files (FF), and The developer considered the smells Force Update (FU) and Props in
number of components of the selected project. Initial State (PIS) severe. However, they are also in third-party mod-
For this particular validation, we instrumented ReactSniffer to gen- ules and will not be refactored. For example, he added the following
erate as output a csv file containing detailed information about each comment to justify these smells instances:
smell, such as the smell name, the smell file path, and the line of code
where the smell was detected. We also added a column to this csv file, They are grave and should be rated five. However, they are all in third-
asking the developer to rate the relevance of each smell in a 5-point party modules, so they will not be refactored, as it is not the project’s
scale, where 1 means the smell is not important at all and probably fault.
will not be refactored and 5 means the smell is very important and
therefore it should be refactored in the near future. In the last column,
Summary: ReactSniffer detected 157 instances of the proposed
the developer was invited to add comments about each detected smell,
smells in a commercial React-based project. According to an expe-
in case he found it relevant. rienced developer, the relevance of such smells in a 5-point scale
ReactSniffer detected 157 smells instances. However, the developer range from 2.2 (JSX Outside the Render Method) to 3.7 (Large File).
did not evaluate 24 of such smells for two key reasons: 15 smells
occurred in deprecated code (i.e., code responsible for features that are
not used anymore in the project) and nine smells occur in code required
9. Discussion
by third-party modules (i.e., the developer sees this code as external to
their project).
Code smells are a widely studied concept, not only in object-
Fig. 8 shows the number of instances of each remaining smell. oriented designs but also in particular domains, such as MVC-based
The most common smell was Large Component (LC), with 45 instances applications [10], JavaScript [6], HTML [7], and CSS [8]. Therefore, a
(28%). In the other extreme of the chart, there are no occurrences of key discussion relates to the novelty of smells we identified for React
the Inheritance Instead of Composition (IIC) and Uncontrolled Component systems. In this regard, in Section 3.3 we have classified our smells into
(UC) smells. three major categories: Novel, Partially Novel, and Traditional ones.
Table 10 shows the average relevance scores for each smell, as Another discussion refers to the small number of some smells, as
answered by the contacted developer. All smells have an average rele- detected in the field study. Although they indicate well-known prob-
vance score greater than 3.3. The exception is JSX Outside the render lems in React apps, we detected few cases of Direct DOM Manipulation,
method (JSX), with an average score of 2.2. The developer justified that Uncontrolled Components, and Inheritance instead of Composition. How-
most JSX occurrences are correlated and caused by two other smells: ever, this might be explained by the fact that our dataset is composed
Large Component (LC) and Too Many Props (TP). For example, he added of popular projects, which might follow more strict programming and
the following comment to justify some JSX instances: design guidelines.
Particularly, it is not easy to provide the precise reasons for the
As the components were very large, developers decided to separate some low removal rate of Inheritance instead of Composition. However, we
chunks of JSX into non-render methods to organize the code. Then in hypothesize two main reasons for this result: (a) inheritance is a widely-
the render, they would only invoke these methods. known object-oriented mechanism; therefore, developers that already

13
F. Ferreira and M.T. Valente Information and Software Technology 155 (2023) 107111

use inheritance in mainstream object-oriented languages may keep us- 11. Related work
ing this mechanism in their React-based projects; (b) inheritance leads
to a tight-coupling between subclasses and superclasses; however, this There is a large body of papers, chapters, and books on code smells.
coupling does not necessarily cause maintenance problems, including The principal one is a chapter by Fowler and Beck [4], which proposes
bugs, particularly when the classes are responsible for stable and rarely 22 code smells for object-oriented design and associates each one with
changed requirements. For example, 14 out of 20 instances of the a possible fixing refactoring. More recently, code smells have also been
Inheritance instead of Composition smell occur in files that have not been studied for web technologies. However, to the best of our knowledge,
changed in the time frame of our study. no research has focused on code smells associated with the adoption of
Since we focused on React-based Web systems, a discussion that JavaScript front-end frameworks.
arises relates to the possibility of generalizing our smells to other front- In the following subsections, we discuss studies on code smells in
end frameworks. Since these frameworks also rely on components for
Web technologies and detection strategies and tools.
structuring and organizing Web UIs, code smells such as Large Com-
ponent, Duplicated Component, Uncontrolled Components, Large Files, Too
Many Props, Low Cohesion, Direct DOM Manipulation, and Prop Drilling 11.1. Code smells in web technologies
can be easily generalized to these frameworks. It is also possible to find
bad practices that force the update of components or even reload the These works study bad practices in HTML, CSS, JavaScript, and
entire page in applications based on other frameworks. For example, we MVC frameworks. For example, Nederlof et al. [7] investigated devi-
can use forceUpdate() and location.reload() methods when ations from best practices in performance, accessibility, and correct
working with Vue. structuring of HTML documents. As cited in our interviews, the process
Vue recommends using templates to build HTML in most cases (in- of transforming dynamic components into HTML can impact these
stead of JSX, as in React). Despite that, Vue also provides the render topics. For example, dynamic components might not be interpreted
method, which is a closer alternative to templates and also allows the reliably by a wide variety of user agents, including assistive technolo-
usage of JSX. Therefore, we can generalize the JSX Outside the Render gies, therefore impacting accessibility. Mazinanian et al. [8] propose an
Method smell to Vue-based systems. Finally, the Inheritance instead of automated approach to remove duplication in CSS code. Their approach
Composition smell also applies to Vue, since Vue provides extension and detects three types of CSS declaration duplication and recommends
mixins mechanisms to reuse features. refactorings to eliminate each one. The authors show that duplication in
Therefore, this preliminary analysis reveals that it possible to gener- CSS is widely common. Since the main way to style React applications
alize most of our detected smells to other frameworks, particularly Vue. is by writing CSS styling separate from JSX files, their work also applies
However, we acknowledge that a further analysis should be conducted to React applications. However, none of theses works focused on smells
in this direction, possibly also considering other frameworks, such as specific to the usage of JavaScript front-end frameworks. In our RQ1,
Angular and Svelte. we filled this gap by answering what are the most common code smells
in React.
10. Threats to validity Fard and Mesbah [6] propose a set of 13 JavaScript code smells
and a code smell detection technique, called JSNOSE. They investigate
In this section we discuss threats to the validity of our results [27].
the occurrence of these smells in 11 Web applications and show that
Since we started with a list of smells from grey literature documents,
Lazy Object, Long Method/Function, Closure Smells, Coupling between
our results and observations may include questionable smells, threat-
JavaScript, HTML, CSS, and Excessive Global Variables are the most
ening validity. However, to reinforce the validity of our findings, we
prevalent smells. Interestingly, they include HTML in JavaScript and
only selected articles that attend to at least one of the ‘‘authority of
JavaScript in HTML as code smells, which is exactly one of the key
the producer criteria’’, proposed by Garousi et al. [15]. Moreover, we
characteristics of modern JavaScript front-end frameworks. For exam-
only considered smells cited by more than one document. In a second
ple, React allows writing HTML in JavaScript, and Vue allows writing
step, all smells were validated with professional React developers.
Finally, we conducted a field study that showed the collected smells JavaScript in HTML.12
are prevalent in open-source systems. Aniche et al. [10] present a catalog of six smells for Web-based MVC
Another threat relates to the developers’ interviews. Some develop- applications. To define the catalog, the authors rely on interviews with
ers may be concerned on stating that a bad practice was common in Spring developers (Spring is a popular Java-based MVC framework).
their company. To minimize this threat, we allowed the participants They show that the proposed smells are more subjected to changes and
to comment on any smells they observed while working with React, defects and that developers indeed perceive them as relevant problems.
regardless of place and time. The authors claim the proposed smells can be generalized to other
The values chosen as thresholds can also threaten the validity of this frameworks, although they are more related to object-oriented design
study. To minimize this thread, we decided to be conservative in our than to SPA-related technologies. For example, Brain Repository, Labo-
thresholds selection policy and used the 90th percentile value of each rious Repository Method, and Fat Repository smells refer to persistence
metric. Moreover, we also achieved promising results using these same classes. The other smells are Promiscuous Controller, Brain Controller,
thresholds when we asked an experienced developer to evaluate the and Meddling Service, which refer to the back-end layer of Web-based
smells detected by ReactSniffer in a commercial project. Specifically, systems.
the average relevance score was 3.16 (in a scale from 1 to 5). As can be found in Fard and Mesbah [6] (but for code smells in
A final threat relates to decisions that may affect our experimental JavaScript) and Aniche et al. [10] (but for code smells in MVC archi-
results. As usual in empirical software engineering studies, our dataset tectures), in our RQ2, we show which React smells are most common
might not represent the whole population of React-based projects. For in real-world projects. Finally, Aniche et al. [10] also investigate when
example, we selected only 10 GitHub projects (plus one closed project, these smells are introduced and how long it takes to refactor/remove
which we used to provide a first validation of our results with a them from a sample of Java-based systems. Similarly, R3 shows how
professional software developer). Therefore, future studies might also often the identified React smells are removed.
include more closed projects. Moreover, as a complementary selection
criteria, future studies might also consider the proportion of React-
based code in a repository. This extra criteria might contribute to 12
However, this is not a surprise, considering that web development
select projects that heavily depend on React to build their front-end technologies have drastically changed since when the article was published
components. (2013).

14
F. Ferreira and M.T. Valente Information and Software Technology 155 (2023) 107111

11.2. Detection strategies and tools Declaration of competing interest

An essential point when detecting code smells regards the defini- The authors declare that they have no known competing finan-
tion of the thresholds for the selected metrics. Some authors propose cial interests or personal relationships that could have appeared to
thresholds based on their experience only. For example, in the seventies influence the work reported in this paper.
McCabe et al. [28] proposed the value ten as a threshold for the
Cyclomatic Complexity metric based on his past experience. Other Data availability
approaches, like ours, use real-world software systems to derive metric
thresholds [29–31]. For example, Alves et al. [21] determine thresholds
Data will be made available on request.
empirically from measurement data of a benchmark of software systems
and derive the threshold values by choosing the 70%, 80%, and 90%
Acknowledgments
percentiles. Aniche et al. [10] use the third quartile (3Q) and the
interquartile range (3Q - 1Q) to define the thresholds. and approaches
that derive relative thresholds from real systems based on a statistical Our research is supported by CNPq, FAPEMIG, and CAPES.
analysis [32,33]. Oliveira et al. [32,33] propose an empirical method
for extracting relative thresholds from real systems based on a statistical References
analysis.
[1] M. Mikowski, J. Powell, Single Page Web Applications: JavaScript End-To-End,
As described in Section 5, we used as a threshold the 90th percentile
Manning Publications Co., 2013.
of each metric distribution. [2] F. Ferreira, H. Borges, M.T. Valente, On the (Un-)adoption of JavaScript front-end
There are also approaches that rely on evolution history informa- frameworks, Softw. - Pract. Exp. 52 (2022) 947–966.
tion to detect the smells, such as HIST (Palomba et al. [22]) and [3] M. Bajammal, D. Mazinanian, A. Mesbah, Generating reusable web components
DECOR (Moha et al. [34]), and approaches that use Deep Learning from mockups, in: 33rd IEEE/ACM International Conference on Automated
(Fakhoury et al. [35]; Liu et al. [36]). Software Engineering, ASE, 2018, pp. 601–611.
[4] M. Fowler, Refactoring: Improving the Design of Existing Code, Addison-Wesley
Existing tools to detect smells in Web languages and frameworks
Professional, 1999.
generally use static analysis or a blend of static and dynamic analysis. [5] A. Hora, Googling for software development: What developers search for and
For example, JSNose [6] applies static and dynamic analysis for detect- what they find, in: 18th IEEE/ACM International Conference on Mining Software
ing code smells in JavaScript code. Cilla [37] is a tool that also relies on Repositories, MSR, 2021, pp. 1–12.
dynamic analysis for detecting unused CSS code. Another related tool [6] A.M. Fard, A. Mesbah, JSNOSE: Detecting JavaScript code smells, in: 13th
is WebScent [38], which identifies six types of Embedded Code Smells International Working Conference on Source Code Analysis and Manipulation,
SCAM, 2013, pp. 116–125.
in Dynamic Web Applications.
[7] A. Nederlof, A. Mesbah, A. van Deursen, Software engineering for the web: The
state of the practice, in: 36th International Conference on Software Engineering,
12. Conclusion ICSE, 2014, pp. 4–13.
[8] D. Mazinanian, N. Tsantalis, A. Mesbah, Discovering refactoring opportunities
Code smells were first proposed for mainstream object-oriented in cascading style sheets, in: 22nd ACM SIGSOFT International Symposium on
languages. For example, the examples presented in Fowler and Beck’s Foundations of Software Engineering, FSE, 2014, pp. 496–506.
[9] L. Vegi, M.T. Valente, Code smells in Elixir: Early results from a grey literature
original catalog of smells are implemented in Java [4]. However,
review, in: 30th International Conference on Program Comprehension, ICPC,
software engineering and related technologies have evolved consid- 2022, pp. 1–5.
erably in the last decades. Particularly, web-based systems evolved [10] M. Aniche, G. Bavota, C. Treude, M.A. Gerosa, A. van Deursen, Code smells
to include full and non-trivial applications running in the browsers, for model-view-controller architectures, Empir. Softw. Eng. 23 (4) (2018)
which are implemented using frameworks such as Facebook’s React. 2121–2157.
Therefore, as the key implication of our study we showed that React- [11] V. Garousi, M. Felderer, M.V. Mäntylä, The need for multivocal literature reviews
in software engineering: complementing systematic literature reviews with grey
based applications include new and specific smells that are not covered
literature, in: 20th International Conference on Evaluation and Assessment in
in general-purpose catalogs. We claim this finding can help front-end Software Engineering, EASE, 2016, pp. 1–6.
developers – who represent 27% of the developers according to recent [12] T. Barik, B. Johnson, E. Murphy-Hill, I heart Hacker News: Expanding qualitative
surveys – to better maintain and improve the quality of their code. research findings by analyzing social news websites, in: 10th Joint Meeting on
React is a very popular JavaScript front-end framework. It is now Foundations of Software Engineering, FSE, 2015, pp. 882–885.
[13] F. Kamei, I. Wiese, C. Lima, I. Polato, V. Nepomuceno, W. Ferreira, M. Ribeiro,
used to implement complex and Reactive Web interfaces, which can
C. Pena, B. Cartaxo, G. Pinto, et al., Grey literature in software engineering: A
reach thousands of lines of code. Therefore, it is important to assure critical review, Inf. Softw. Technol. 1 (1) (2021) 1–26.
the maintainability of React-based applications. In this paper, by using [14] H. Zhang, X. Zhou, X. Huang, H. Huang, M.A. Babar, An evidence-based inquiry
a grey literature review and by interviewing professional React devel- into the use of grey literature in software engineering, in: 42nd International
opers, we derived a list of 12 code smells for React-based apps. We Conference on Software Engineering, ICSE, 2020, pp. 1422–1434.
provided examples for each smell; implemented a tool to detect them; [15] V. Garousi, M. Felderer, M.V. Mäntylä, Guidelines for including grey literature
and conducting multivocal literature reviews in software engineering, Inf. Softw.
and used this tool in a sample of ten GitHub projects, when we were
Technol. 106 (1) (2019) 101–121.
able to detect 2,565 smells instances, covering nine out of 12 smell [16] D.S. Cruzes, T. Dyba, Recommended steps for thematic synthesis in software
types proposed in the paper. As future work, we plan to consider new engineering, in: 5th International Symposium on Empirical Software Engineering
frameworks, such as Vue.js, Angular, and Svelte. This is particularly and Measurement, ESEM, 2011, pp. 275–284.
important to provide a general catalog of smells for front-development [17] D.S. Cruzes, T. Dybå, Research synthesis in software engineering: A tertiary
study, Inf. Softw. Technol. 53 (5) (2011) 440–455.
and therefore to avoid the explosion of smells for a wide range of
[18] J. Ousterhout, A Philosophy of Software Design, Yaknyam Press, 2018.
frameworks. We also plan to extend our prototype tool to handle all [19] E. Gamma, R. Helm, R. Johnson, J. Vlissides, Design Patterns: Elements of
identified smells. Reusable Object-Oriented Software, Addison-Wesley, 1994.
[20] R.C. Martin, J. Grenning, S. Brown, Clean Architecture: A Craftsman’s Guide to
CRediT authorship contribution statement Software Structure and Design, Prentice Hall, 2018.
[21] T.L. Alves, C. Ypma, J. Visser, Deriving metric thresholds from benchmark data,
Fabio Ferreira: Conceptualization, Methodology, Software, Valida- in: 26th IEEE International Conference on Software Maintenance, ICSM, 2010,
pp. 1–10.
tion, Investigation, Data curation, Writing – original draft, Writing –
[22] F. Palomba, G. Bavota, M.D. Penta, R. Oliveto, A.D. Lucia, D. Poshyvanyk,
review & editing. Marco Tulio Valente: Conceptualization, Methodol- Detecting bad smells in source code using change history information, in: 28th
ogy, Data curation, Writing – original draft, Writing – review & editing, IEEE/ACM International Conference on Automated Software Engineering, ASE,
Supervision, Project administration, Funding acquisition. 2013, pp. 268–278.

15
F. Ferreira and M.T. Valente Information and Software Technology 155 (2023) 107111

[23] J. Pantiuchina, M. Lanza, G. Bavota, Improving code: The (MIS) perception of [32] P. Oliveira, M.T. Valente, F. Lima, Extracting relative thresholds for source
quality metrics, in: 34th IEEE International Conference on Software Maintenance code metrics, in: IEEE Conference on Software Maintenance, Reengineering and
and Evolution, ICSME, 2018, pp. 80–91. Reverse Engineering, CSMR-WCRE, 2014, pp. 254–263.
[24] M.Ó. Cinnéide, L. Tratt, M. Harman, S. Counsell, I.H. Moghadam, Experimental [33] P. Oliveira, F. Lima, M.T. Valente, A. Serebrenik, RTTool: A tool for extracting
assessment of software metrics using automated refactoring, in: 6th ACM/IEEE relative thresholds for source code metrics, in: 30th International Conference
International Symposium on Empirical Software Engineering and Measurement, on Software Maintenance and Evolution, ICSME, Tool Demo Track, 2014, pp.
ESEM, 2012, pp. 49–58. 629–632.
[25] H. Borges, A. Hora, M.T. Valente, Understanding the factors that impact the [34] N. Moha, Y.-G. Guéhéneuc, L. Duchien, A.-F. Le Meur, Decor: A method for the
popularity of GitHub repositories, in: 32nd IEEE International Conference on specification and detection of code and design smells, IEEE Trans. Softw. Eng.
Software Maintenance and Evolution, ICSME, 2016, pp. 334–344. 36 (1) (2009) 20–36.
[26] H. Silva, M.T. Valente, What’s in a GitHub star? Understanding repository [35] S. Fakhoury, V. Arnaoudova, C. Noiseux, F. Khomh, G. Antoniol, Keep it simple:
starring practices in a social coding platform, J. Syst. Softw. 146 (1) (2018) Is deep learning good for linguistic smell detection? in: SANER, 2018, pp.
112–129. 602–611.
[27] C. Wohlin, Experimentation in Software Engineering, Springer Science & Business [36] H. Liu, J. Jin, Z. Xu, Y. Bu, Y. Zou, L. Zhang, Deep learning based code smell
Media, 2012. detection, IEEE Trans. Softw. Eng. (2019) 1–28.
[28] T.J. McCabe, A complexity measure, IEEE Trans. Softw. Eng. 1 (4) (1976) [37] A. Mesbah, S. Mirshokraie, Automated analysis of CSS rules to support style
308–320. maintenance, in: 34th International Conference on Software Engineering, ICSE,
[29] M. Lanza, R. Marinescu, Object-Oriented Metrics in Practice: Using Software 2012, pp. 408–418.
Metrics to Characterize, Evaluate, and Improve the Design of Object-Oriented [38] H.V. Nguyen, H.A. Nguyen, T.T. Nguyen, A.T. Nguyen, T.N. Nguyen, Detection
Systems, Springer Science & Business Media, 2007. of embedded code smells in dynamic web applications, in: 27th IEEE/ACM
[30] F.A. Fontana, V. Ferme, M. Zanoni, A. Yamashita, Automatic metric thresholds International Conference on Automated Software Engineering, ASE, 2012, pp.
derivation for code smell detection, in: 6th International Workshop on Emerging 282–285.
Trends in Software Metrics, WETSoM, 2015, pp. 44–53.
[31] P. Oliveira, M.T. Valente, F. Lima, Extracting relative thresholds for source
code metrics, in: IEEE Conference on Software Maintenance, Reengineering and
Reverse Engineering, CSMR-WCRE, 2014, pp. 254–263.

16

You might also like