React Native, the elder sibling of React, was launched in the year 2011. It was launched to develop user interface templates using JS thread. Today, we’re going to talk about the React Native Best Practices in detail.
A natural extension of React is RN, which was launched in the year 2015. A single logline that encapsulates the core functionality of RN is that: it is a cross-platform that helps to build native applications using JavaScript, and Native components developed though React.
React Native though being a new platform has garnered a lot of attention. It is estimated that nearly 1/10th of apps that are available on the app store are built using RN.
Now, working with RN brings forth two types of app development mechanism: traditional apps, and Expokit based apps. React Native App Examples are proof of that.
Best practices while developing a React Native App
With React Native, the goal is to achieve a faster and easily native mobile application. But not everyone can come up with a brilliant Native app.
Well, the basic rule for developing a robust native application lies in proper understanding and execution of React Native best practices.
Divide The Components
One of the best React Native practices that developers can follow is to split the React Native components into two individual directories: container and plain components. It is one of the React Native Best Practices that one should follow.
We have listed the rules that each form of directories follow:
Component
- Use of redux hooks which otherwise would not fit into container rules. Must be out into use here.
- Imports relating to styles, type, and components, which feature code reuse property must be included here.
- The JavaScript codes of the RN components coupled with the concerned RN based imports must be included here.
// components import React, { useRef, useState } from 'react' import { Animated, View, TouchableOpacity, Text, } from 'react-native' import { ArrowIcon } from 'components/icons' import NutritionDetails from './NutritionDetails' import type { NutritionalInfoType } from 'types/product' import { nutritionalInfoStyles as styles } from './styles'
Container
- The react native imports from the respective RN component JSX code must not be placed in the container.
- The imports relating to higher-order components having relation with Redux store elements such as redux hooks, redux selectors must be placed into the container directory.
// containers import React from 'react' import type { Element } from 'react' import { useDispatch, useSelector } from 'react-redux' import { NavigationScreenProp } from 'react-navigation' import I18n from 'react-native-i18n' import { Formik } from 'formik' import { registerProcess } from 'actions' import { authenticationSelector, ordersLoading } from 'selectors'
Create Aliases
Framing Aliases is an apt way to get around the issue of nested imports like: Imports Product from ‘../../../Components/Product’.
You can create aliases using babel-plugin-module-resolver.
Besides framing aliases for target files and the directories, the babel plugin makes the addition of new root directories that contain the module.
alias: { actions: './app/actions', api: './app/api', assets: './app/assets', components: './app/components', containers: './app/containers', constants: './app/constants', sagas: './app/sagas', selectors: './app/selectors', types: './app/types', utils: './app/utils', }
Sort The Imports
Sorting imports helps to review your code more efficiently. It is a good practice to divide the imports. This division of imports must follow a logical pattern.
One of the generic ways to divide and sort the imports is to categorize them according to origin i.e Library or other sources of import. The code snippet mentioned below will give you a better idea.
import React, { useState } from 'react' import type { Element } from 'react' import { View, ScrollView } from 'react-native' import { NavigationScreenProp } from 'react-navigation' import { useSelector } from 'react-redux' import type { OrderType, LineItemWithProdType } from 'types' import { ordersByIdSelector, productsByIdSelector } from 'selectors' import { OrderTicket, OrderDetails } from 'components/Order' import { DefaultStatusBar, RedStatusBar } from 'components/StatusBar' import { orderStyles as styles } from './styles
Declare The Types
Declaring type is essential regardless of the programming language that you are using. It can either be Flow or Typescript.
By declaring, we essentially refer to declaring the form of type i.e. either a return type or an argument type.
const payload: LoginUserType = { email: '[email protected]', password: 'password', } const roundDistance = (distance: number): string => (distance / 1000). toFixed(1)
Now, there is a basic rule to declare types, though you might be pulled into the idea of declaring types for every new component for ease in management, it turns into a pitfall when left unorganized in a separate types directory.
On a side note, When you use flow, it is essential to include ‘// @flow’ for every file that you have created while working on a new project.
type LoginUserType = {| email: string, password: string, |}
Separate The Styles
When you decide to work in React Native, you need to understand how to use the styles. This style gives the application identity.
Among the various ways to deal with style, the React Native styling best practice is to separate the styles from the components.
Normally, to use styles better developers import the stylesheet from the React native library to create a separate style object.
There are various other ways to separate styles and components, but the basic benefit of doing so is that the codebase looks neat and easy on the eye of the reviewer for further analysis.
import { productAmountstyles as styles } from './styles' ... <View style={styles.container}> <Text style={styles.amountText}>{I18n.t('itemScreen.amount')}</Text> <View style={styles.quantityContainer}> <CircularButton disabled={quantity === 1} onPress={onReduce} /> <View style={styles.quantityTextContainer}> <Text style={styles.quantityText}>{quantity}</Text> </View> <CircularButton disabled={false} add onPress={onAdd} /> </View> </View>
Components Must Hook
A function component is generally characterized to have no state or lifecycle method.
This is where hooks come into the picture, with hooks, you can introduce both state and lifecycle elements to the functional component.
In fact, for this you do not need to create a class component, sounds uncanny right? However, this is one of the most effective React Native Best Practices.
So, consider this scenario, you are writing a function component, and you realize that there is a need to add a state, in such cases hooks come off as a great helping tool.
const [showModal, setShowModal] = useState(true) ... useEffect(() => { dispatch(doFetchOrders()) dispatch(doFetchProducts()) }, [dispatch]) ... const authenticationData: AuthenticationStateType = useSelector (authenticationSelector) ... const mapViewRef: { current: MapView } = useRef(null)
Let Redux Manage
Through the journey React, the platform has been pooled together with various app state management platforms, one of the popular ones is Redux
Some feel that Redux is overpowering and has lost its utility factor over time. But Redux still holds the key to React Native app state management quickly and effectively.
The functional aspect of Redux can be bolstered with ‘immer’ that works on a copy-on-write mechanism to gauge the changes in the draft form.
You can also pair it up with React Native debugger which helps in viewing the app state tree. It also helps to execute redux based actions to see changes in UI brought henceforth.
Write sagas for asynchrony
Redux is generally programmed to work in a synchronous pattern which looks something like this: Action dispatch-followed-by-reducer acting to change the store- followed by- repeat.
This is a fixed pattern in which redux works. Now, anything that falls out of the natural flow of the Redux is called a side-effect.
export function* loginProcess({ payload: session }) { const { login } = url yield put(doAuthenticateUserStart()) const data = yield call(makeApiCall, login, { session }) if (data.message || data.errors) { yield put(doLoginUserError(data)) } else if (data) { yield put(doLoginUserDone(data)) yield call(continueOrderingProcess) } }
Now, the question is how do you add these foreign elements? It is where Redux saga comes into play with its middleware capabilities.
The Redux-saga uses the ES6 feature called generator to neatly include asynchronous code which resembles to be in a synchronous format.
Also Read: React Native vs Cordova
Aggregate The Selectors
The basic definition of selectors may seem to be a bit twisty: it is a function that accepts the Redux state in the form of an argument and routes the data back which is extracted from the state.
To put it plainly, selectors are functional elements that are added to tracking and retracting snippets of data from the internalized components of a Redux store.
Creating an aggregated selectors directory helps in reusing the functions across several components.
The developers can extend the select philosophy, and employ the reselect library which comes with innate benefits of memorization and cache.
export const productsSelector = state => state.menuItems.products.map(({ product }) => product) export const productsByIdSelector = createSelector( productsSelector, (products) => products.reduce((prodObj, product) => ( prodObj[product.id] = product), {}), ) ... const productsById = useSelector(productsByIdSelector)
Jest Test Them
Working on React and React Native framework calls for a specifically created testing platform to track and fix the bug within a short period.
In comes, Jest: it is a javascript library that operates on an open-to-all basis for testing codes. There are various methods to test React Native apps. The two of the common ones are Snapshot testing and Redux testing.
it('renders ArrowIcon', () => { const component = renderer.create(<ArrowIcon />) const tree = component.toJSON() expect(tree).toMatchSnapshot() })
With the snapshot testing, you can get a snapshot of the string which showcases the details of the rendered element.
Further, it is saved in a specified file to compare the changes in the UI. And if the UI remains the same it indicates that the script used does not have any bugs.
Redux testing is one of the redux best practices, as with such forms of testing, you can track the changes initiated in the state of apps. You can check for changes in a predictable fashion.
/ actions it('gets all products', () => { const action = doFetchProducts() const expectedAction = { type: 'PRODUCTS_FETCH' } expect(action).toEqual(expectedAction) }) // reducers it('should handle FORGOT_PASSWORD_DONE', () => { const forgotPasswordDone = { type: FORGOT_PASSWORD_DONE, payload: { email: '[email protected]', }, } Object.freeze(beforeForgotPasswordDone) const newStateAfterForgotPassword = authReducer(beforeForgotPasswordDone, forgotPasswordDone) expect(newStateAfterForgotPassword).toEqual(afterForgotPasswordDone) }) // selectors it('productsByIdSelector should return all the products by Id', () => { expect(productsByIdSelector(mockedState)).toEqual(productsById) })
Classify The Components
For creating a better developing ecosystem in the React Native it is essential to classify the components according to their nature: presentation component.
This component is quite similar to a stateless component, The second classification of component is Container component that resembles stateful component.
This is one of the most powerful React Native Best Practices that a developer should follow.
Presentational component
- Primarily deals with how a component looks in the final UI framework
- Does not provide info about data loading and mutable option
- Uses props to callback and receive data
Container component
- It contains info about the component
- Arms the presentational component with stateful properties
Using Functional Components For Stateless
Much like the stateless component which is mainly a responsible UI design framework, it does not have any state, or info about the component. Similarly, the functional components also do not have a state.
A functional component is a javascript function that receives props (properties) as input and renders output in the form of HTML guised in the form of JSX. Again, such components do not involve any lifecycle method, and they are mainly concerned with the UI.
import React from 'react'; import { Text, View } from 'react-native'; const Button = ({ btnName }) => { const { containerStyle, textStyle } = styles; return ( <View style={containerStyle}> <Text style={textStyle}>{btnName}</Text> </View> ); }; const styles = { containerStyle: { flex: 1, justifyContent: 'center', alignItems: 'center', margin }, textStyle: { textAlign: 'center', fontSize: 25, fontFamily: fonts.bold, color: colors.grayLight, margin } }; export default Button;
Using Class Components For Stateful
Class components have their core in the ES6 classes, which much like the functional component receive props as input and churn out HTML as output.
The ES6 has details of the component, they can be considered as a stateful component.
It has the functionality details and the lifecycle method of the component. Some of which include- component willUnmount, the component will update, and various others.
import React from 'react'; import { Text, View } from 'react-native'; const Button = ({ btnName }) => { const { containerStyle, textStyle } = styles; return ( <View style={containerStyle}> <Text style={textStyle}>{btnName}</Text> </View> ); }; const styles = { containerStyle: { flex: 1, justifyContent: 'center', alignItems: 'center', margin }, textStyle: { textAlign: 'center', fontSize: 25, fontFamily: fonts.bold, color: colors.grayLight, margin } }; export default Button;
The basic utility of the container component treads on the line of a state component and fetches data to be rendered to the subcomponent. Thus, it makes sense to mold it into a class component.
Key of Each Element Should Be Unique
Lists are an essential part of any application. There are numerous use-cases enveloped in a list. It is one of the most important React Native Security Best Practices.
Adhering to such lists has a direct impact on the performance of the app, as it can slow down the app’s functionality. Thus, it is advised to use a unique key for each element in the list.
const todoItems = todos.map((todo) => <li key={todo._id}> {todo.text} </li> );
The most efficient way to add a unique key to each element is to assign a particular id. An ‘id’ is assigned to each element making it simple to represent changes for a particular element. The key guises in the form of a unique string that specifies each element.
Manage Static Images
Developing an app in React Native involves handling various forms of static media assets, which are displayed with ease across iOS and Android platforms. As per the RN doc, if you want to add a static image, you must specify it using the ‘require’ tag in the source code tree.
Using the ‘require’ tag, you can easily import the image to the source attribute of the concerned image component.
The repression of the required tag is given below.
<Image source={require(‘./my-icon.png’)} />
There are several advantages of specifying the image using ‘require’. For instance, it fetches the right images adhering to dimension without really going down the path of image collision due to the naming convention.
// BAD var icon = this.props.active ? 'icon-innofied-active' : 'icon-innofied-inactive'; <Image source={require('./' + icon + '.png')} />; // GOOD var icon = this.props.active ? require('./icon-innofied-active.png') : require('./icon-innofied-inactive.png'); <Image source={icon} />;
Optimizing React Native Images
Apps build under the React Native framework showcase an advantage of smooth and quick response. Such apps are attuned to deliver high performance.
But certain elements bring in performance issues. One such element is the use of the image in its raw form.
The images used in the React native ideally should be resized for achieving a better load time. It is also advised to store the resized images in the cloud for easy access.
Using a ‘cdn’ link to route the image back to API can be a fruitful way to improve the load time.
Feedback/Highlight Facility In Gesture System
It is important to ascertain how the user is willing to interact with a particular component/button in the app. Either they want to scroll/slide/swipe.
For a better understanding and implementation, React Native comes with a Gesture Responder System. It maps out the lifecycle of a gesture.
Generally, it is advisable to add visual feedback for a gesture touch, so that users are wary of the outcomes. The visual feedback is powered by animation.
Also, the cancel-ability i.e., to abort when the finger touch is moved away from the elements must be included.
Read More: Exploring The Reasons To Use Ruby On Rails With React
Using Platform Specific Code & Style
The potential of React Native was to develop a cross-platform native application sharing the codebase. Using platform specific code is one of the effective React Native Best Practices.
This allowed us to develop an application that fits both iOS and Android versions. It allows the teams that were set aside to develop for the dual-platform to work in tandem.
Having stated that there remains scope for using platform-specific code for initiating style sheets and callbacks, these are just a few areas where you might need platform-specific code.
import { Platform, StyleSheet } from 'react-native'; const styles = StyleSheet.create({ container: { flex: 1, ...Platform.select({ ios: { fontFamily: 'Arial', }, android: { fontFamily: 'Roboto', }, }), }, });
There are two specific APIs: Platform.OS and Platform.select. These APIs help to gauge the native platform and apply appropriate styles.
Locking Dependencies
Using third party libraries or dependencies requires the developer to have some sort of method to keep track of changes (versions). It is one of the most powerful React Native Security Best Practices.
Yes, dependencies make the task of developing an app more holistic and easier, but at the same time, you must employ dependencies that are of utmost necessity.
Now, when adding dependencies based on regular needs, managing them becomes an uphill task. Thus, if you add a dependency, you must lock the range or the versions.
It saves a lot of time and effort. Staying attuned to changes is quite tough, especially with Javascript as things (changes) move fast in Js library.
"react-native-img-cache": "^1.5.3", "react-native-fetch-blob": "^0.10.7", "react-native-linear-gradient": "^2.3.0"
Instead of this:
"react-native-img-cache": "1.5.3", "react-native-fetch-blob": "0.10.7", "react-native-linear-gradient": "2.3.0"
Use this.
Taking UI Framework Support
The basic idea of React was to develop a UI framework that delivers the best user experience. It was built to augment the process of creating UI templates using JS thread.
So, it makes complete sense to use the UI framework, to flesh out the related elements in real quick time.
The most commonly used UI framework is Galio.io. Such a platform not only augments the rate of build, but it also provides for selecting specific components.
These specific components in the absence of a robust framework would have been hard to track and include in the build.
Don’t Put Logs In Release Build
Putting logs in release builds is a no-no, as it slows down your app. This true for most of the logger types present out there, especially if you work in a redux logger.
Excess use of the console.log statement which initiates the thread of log for keeping track of events and warnings is the cause for slowing down the application.
A Console.log statement is for debugging purposes, as this statement displays the data on for tracking errors. While creating logs is easy, it is necessary to disable for the release build setup.
Use Safe Area View
In the newer versions of the iPhone, the sensor and the home button overlap with the nav and tab bar components. It is one of the effective React Native Security Best Practices.
This creates a bad user experience and also the interface looks murky. To solve this issue React Native introduced something called safe area view.
The moment you import the safe area view from the RN and substitute it for the view tag, the UI overlaps disappear. Now, you have to import the SAV for each button component i.e the tab and the nave bar.
Also, in the landscape mode, the content seems to overlap with the sensor cluster, so in the view tag for content, you need to import the safe area view.
Don’t Use TouchWithoutFeedback
Generally, the buttons component in React Native does not have much to offer in terms of real-time display of controls. To tackle this issue, the RN includes touchable button components in various forms.
The list includes: TouchableHighlight, TouchableOpacity, Touchable Native Feedback, and finally touchable without feedback, this is where the developer needs to be extremely cautious.
The docs available on the RN official website states you should have a good reason for using TouchWithoutFeeedback, as each element must exude visual feedback when clicked.
You can use this touchable element when you do not want to trigger animated feedback when initiating action.
Keep A Folder Structure
Keeping a folder structure is one of the React Native structure’s best practices. While working on a small project, you might get away with minimum attention to organize your folders.
There are a number of components into consideration. But things get trickier when you work on a bigger project that involves numerous components.
As the codebase evolves, managing each component folder becomes a mess. It is in such times, you can appreciate the organized fashion of creating and managing folders. Yes, there is such an approach to organizing folders, but there are plenty of versions of the same.
The commonly used approach of organizing folders is to fit in all elements that are responsible for the UI presentation on the screen folder, and on the other hand, placing all the state-based files in their respective modules.
Don’t Use Expo
Expo does bring in several advantages to the table and there is no denying this fact. For instance, with Expo, the developers can carve a fully functional cross-platform native without having to learn Xcode or Java (Android studio).
On the other end of the spectrum, best developers mostly overlook the flaws of using the Expo. As with this platform, it is difficult to wrap around a solution without native code.
To put it simply, you cannot use a package of native language origin that requires linking. Also, there is a structuring of the framework over the cloud and not on a local setup.
Conclusion
‘Don’t use Expo’ concludes our list of best React Native practices, we have covered a wide spectrum of best practices concerning structure, style, and various other things.
But one thing that tends to go off the radar is the React Native security best practices like SSL linking and secure storage. A proper follow-up with the official React Official Document for best practices will give a more clear idea.
We hope you had a great time reading this article and it proves to be of great value if you will hire react native developers in the future. Thank You.!
-
What Are Best Practices For React Native?
There are many best practices that a developer needs to follow while working with React Native. They are as follows:
Divide The Components
Create Aliases
Sort The Imports
Declare The Types
Separate The Styles
Components Must Hook -
What Things You Shouldn’t Do While Working With React Native?
There are plenty of Don’ts when it comes to React Native. Some of the most popular ones are as listed below:
Don’t Put Log In Release Build
Don’t Use Expo
Don’t Use TouchableWithoutFeedback -
Why Should Developers Follow The Best Practices?
The reason why developers should follow the best practices is that it helps them to improve the performance of the app. It also helps them to detect the errors pretty quickly and also, create a hassle-free environment.
-
Why React Native Is Utilized A Lot Nowadays?
Nowadays, it’s about getting the quick results within a limited time frame and within budget constraints. That’s where React Native is so good as it allows you to build one app that runs on both Android/iOS .