React Development for Senior Developers — From a Product Point of View
So you now have developed a React application and you’re happy that it is running pretty fine. You have your logic implemented, you have created a sufficient amount of components, you have Node Services running in the backend, the data binding is happening pretty fine and a lot of cool visually appealing stuffs rendering on your UI.
But what next ? Do you think your development is over ?
Hahaha.. The answer is a big NO.
Think from a product perspective dude !! Do you really think that this is the final product that you’re gonna give to your clients ? Well, let me say, there are still few great deeds remaining and in this article, I am gonna explain in brief what are those important stages that you should perform after your code creation.
Please note that I won’t dig in much deeper into each stage, as there are already official documentations readily available which will give you a much detailed information about the ways to implement those. I have also given some hyperlinks throughout the article in order to direct you to the correct source.
So, just adjust your seat, take a cup of coffee and let’s begin.
1. Unit Testing
As developers, it is our job to test each and every component that we create. React comes with Jest as it’s test runner, specially when you have your boilerplate created using Create-React-App. While there are other test frameworks available freely in the web such as Mocha and Karma, most of the react developers love to use Jest, due to it’s high performance and ease. There are various ways of unit testing your components with Jest and the most easiest is Snapshot Testing.
A typical snapshot test case renders a UI component, takes a snapshot, then compares it to a reference snapshot file stored alongside the test. The test will fail if the two snapshots do not match: either the change is unexpected, or the reference snapshot needs to be updated to the new version of the UI component.
While Snapshot Testing tests the render of your components, if you wanna test some specific functions, you can also do that by mocking them.
Now, when it comes to using Redux in our applications, we should not forget to test the reducers and the actions associated.
2. Static Code Analysis
A great philosopher once said,
“It isn’t what you code, but it is how you code”
Anybody can write code. But what makes you a great developer is when you know the good habits of coding. There are many tools available in the web for reviewing your code such as Sonarqube, ESLint, JSlint etc., I personally prefer ESLint, since it comes readily with a VSCode plugin and also as an npm package. It will help you identify the mistakes throughout the application in the form of errors and warnings in the terminal or console.
- Unused variables and imports should be removed from the code, since those will consume unnecessary memory.
- Conditional Statements which use == should be replaced with ===. If required, we can parse either of the sides.
- You often might have observed some warnings thrown in the terminal which mention about missing dependency of useEffect(). We should use the useEffect() hook very carefully in our functional components since improper use of the same may lead to memory leakage issues. It comes with a side-effect or dependency array, which behaves differently for different kind of scenarios. For a more detailed explanation of the side-effect, please refer this article.
- Avoid using inline style attribute and rather use CSS Modules or pre-processors such as SASS.
- Always use code-formatters for formatting your code. This will enhance readability for both you and your team-mates. You can use the prettier npm package and use a script command for it, before building and pushing your application to the repository.
- If you want to hide your code and the source map from the user within your build, you can include the GENERATE_SOURCE_MAP=false in your build script.
“build”: “GENERATE_SOURCEMAP=false react-scripts build” - Use ES6 features such as Arrow Functions etc., instead of traditional JS coding methods.
3. Enabling Caching Mechanism
This is however optional, but I still prefer for some of the applications. We can reduce the number of fetch calls by using the Local Storage of the browser. In such case, the call to the server would only occur once, unless we reload the page.
4. Enable Security
This has become one of the most important aspect that developers must need to take care of while building a web application. Your entire application may collapse if it is insecure.
Now there can be various types of security threats. I have mentioned some techniques that can be used specifically from client-side only.
Security from Cross-Site Scripting (XSS) attacks —
Hackers can inject code in the form of script tags giving them full control over the application running in the user’s browser. This is mostly targeted in the form fields such as input, paragraph and text area.
Remedies:
- Use React’s createElement() API wherever you wish to use <input> or <p> tags .
React.createElement('input', { placeholder: 'Enter a url', type: 'url', autoFocus: true }, 'default_url');
- Use dangerouslySetInnerHTML() — to set HTML directly from React instead of using the error-prone
innerHTML.
<div contentEditable='true' dangerouslySetInnerHTML={{ __html: "Hello" }}></div>
- You can also sanitize your inputs by using a library known as DOMPurify.
- Here you can find a series of XSS attacks that can be used for testing XSS vulnerability in your application.
Never Store JWT Tokens in Local Storage —
Storing JWT or OAuth Tokens in the local storage is a very high risk as someone can easily open the developer tools to steal the tokens and misuse them.
It is better to store the tokens by setting them as HTTP Cookie. Alternatively, you can also move your tokens to the React App’s state.
Never Include Client Secrets in your code —
It’s an unforgivable sin if you use client secret in your Java script code. What you can do is use them in your Environment files, since these files are normally not pushed into the git repository.
Use End-to-End Encryption —
In order to prevent the hacker to see the response coming from the backend API, we can perform end to end encryption through the use of crypto module in both front-end and back-end. For example,
- From the React side, we can first encrypt the URL end-point.
- Use that inside the fetch() API to request for a response from the backend.
- The Backend Server on the other hand receives the encrypted end-point and decrypt it to get the exact end-point or route.
- The Backend Server calls the correct route to get the response.
- The Backend Server then encrypts the response and send it back to the React Server.
- The React App decrypts the response and renders the view accordingly.
Use Content-Security-Policy (CSP) Headers —
Content-Security-Policy is the name of a HTTP response header that modern browsers use to enhance the security of the document (or web page). The Content-Security-Policy header allows you to restrict how resources such as JavaScript, CSS, or pretty much anything that the browser loads.
Although it is primarily used as a HTTP response header, you can also apply it via a meta tag.
CSP was first designed to reduce the attack surface of Cross Site Scripting (XSS) attacks, later versions of the spec also protect against other forms of attack such as Click Jacking.
I found this article very useful for setting up CSP.
Use Nexus Vulnerability Scanner —
Nexus Vulnerability Scanner is a tool that scans your application for vulnerabilities and gives you a report on its analysis.
5. Enable gzip Compression
Gzip is a data compression algorithm capable of compressing and decompressing files quickly. Compression is the simplest way to save network bandwidth and speed up the application.
We can use gzip for compressing big files, images, JS files etc.
On receiving the response from the front-end server, the back-end server extracts the file data and searches for the Accept-Encoding
header to determine how to encode the application.
If the server supports compression using gzip, the resources are compressed and sent over the network. The compressed copies of each resource, with added Content-Encoding
headers, specify that the resource is encoded using gzip.
The browser then decompresses the content into its original uncompressed version before rendering it to the user.
6. Progressive Web App
This is however an optional feature and depends on the requirements. A progressive web app is a web application that provides users with an experience comparable to a native mobile app. Its functionality mimics that of a native app. The objective of a progressive web app is to provide an app-like experience.
You can transform your React application to a PWA through the steps mentioned here.
Conclusion
As the angels of science have said, no machine is perfect and purely efficient. While the similar theory applies for a web application, we should always keep in our mind that technologies are evolving with us. And so, we have to always ensure that we always continue finding out ways to develop and deliver our application which would be the highest in quality, robustness, scalability and performance.
That’s all for this article. I’ll be adding on more stuffs soon. :)