Best Coding Practices in ReactJS
In this post, we'll discuss the best coding practices you can follow in your next project to make the code clean, efficient, and easily adaptable by another developer.
Join the DZone community and get the full member experience.
Join For FreeIntroduction
In this article, we will discuss the best coding practices that you can follow in your next project. These practices will make your code reusable, cleaner, efficient, and easily adaptable by another developer.
Here is a list of the coding best practices that we will cover today:- Combine state
- In long component hierarchy use useContext
- Separate UI and logic
- Remove unnecessary props
- Write a function for a repetitive task
- Avoid named import/member import
- Use forEach instead of map
1. Combine State
In most of the components, you'll have a state. While defining a new state, take time and think if you can combine multiple states into a single state. Let's understand this with the help of an example. Let's say you are working on a chocolate website and you have two types of sizes:
Default size: You will receive sizes from API.
Custom size: Users can add custom sizes.
Once the user has added custom sizes, he/she can proceed for checkout by selecting desired sizes. In the wrong coding practice, you can have three different states.
- State for default sizes (received from backend)
- State for custom sizes
- State for selected sizes
const [APISizes, setAPISizes] = useState([{
id: ''
height: '',
width: ''
}]);
const [customSizes, setCustomSizes] = useState([{
id: '',
height: '',
width: ''
}]);
const [selectedSizes, setSelectedSizes] = useState([1, 2]);
Now you have to keep an eye on three different states and you'll need to keep them in sync. Let's look at scenarios where it will create a problem.
While displaying all sizes you have to loop through two states including APISizes and customSizes.
In selectedSizes we are storing only ids but for size information, we have to iterate over APISize and CustomSize. In good coding practice, you can define a single state as follow.
const [userSuggestions, setUserSuggestion] = useState([{
id: 1,
height: 100,
width: 100,
isCustom: false,
isSelected: false
}]);
In this case, you have to think about only one state and if another developer works on your code it's easy for her/him too. Additionally, If you want to introduce a new key, you have to update only one state instead of 2 or 3 states.
const [userSuggestions, setUserSuggestion] = useState([{
id: 1,
height: 100,
width: 100,
isCustom: false,
isSelected: false,
isByDefaultSelected: true,
}]);
2. In Long Component Hierarchy Use useContext
In a long component hierarchy, useContext will provide cleaner and reusable code. Look at the following example:
In the application, you can select a project and folder. In the dashboard component, we want to show the total selected projects and folders and for that we need to define two states in the dashboard component:
- Selected project
- Selected folder
We will pass these states from:
Selected project: Dashboard -> Projects -> Project Display -> Project Option.
Selected folder: Dashboard -> Folders -> Folder display -> Folder Option.
This becomes nastier as the number of states and the number of components grows. The solution to this problem is creating context. It will allow you to use state in any component. You will call context from the topmost component and all children will be able to use state.
If you don't know how context works, you can go through this article.
3. Separate Logic and UI
It's best to separate logic and UI. For example, you can write onClick function as an inline function or you can call a separate function. Creating a separate function rather than writing an inline function will give a clear separation between UI and logic. It will make the code more understandable, clear, and reusable.
4. Remove Unnecessary Props
Keep a good eye on whether your props are dynamic or static. Other than static props, sometimes we pass redux state as props which don't help in reducing the number of renders at all. Passing redux state as props makes components difficult to reuse. We will understand this with help of an example.
In our project, we have a profile component and the profile component is called the Image component. Image component requires login user information and login user information is stored in redux state. Profile component is already calling a redux state of login information. In this situation, you can choose from two options.
- Pass redux state as props from parent component(Profile) to child component(Image).
- Call redux state in child component(Image) using useSelector.
You should always go for the second option as in the future, the Image component will be used from multiple components. All the parent components of the Image component have to call login user information. (As login user information is compulsory props of Image component). This will lead to an unnecessary call of redux state every time the component is reused.
In both cases, if you pass state as props from parent component (from profile to Image) or use useSelector inside child component (Image component), the React will re-render. A change in props causes re-render and change in the redux state also cause re-render.
5. Write a Function for a Repetitive Task
This seems to be a normal thing but keep a good eye on repetitive code. For example, you might be updating the same state in the same manner from 5 different components. In this case, create one function to update the state and use that function in all the components. Slow down while you write code and if you catch yourself writing the same code, again and again, it's time to write a common function instead.
I highly recommend creating a common function for the repetitive task. As more code you'll write, you will appreciate the time you have spent writing common functions. In the future, if you have any code change, there will be only one place to change code rather than going through all the components.
6. Avoid Named Import/Member Import if Possible
First, let's understand how we can import the module. Let's say you are using material UI. In your component, you need Button and TextField from material UI. You can import them in two ways:
1. Named Import / Member Import
import {TextField, Button} from "@mui/material";
2. Default Import
import Button from "@mui/material/Button";
import TextField from "@mui/material/TextField";
Always prefer default import as in the default import only code of button and textfield is imported. In the named import/member import, all the modules of material UI are loaded. From all the material UI code you are using buttons and textfield in your component. Over time this will increase bundle size. Default import has one disadvantage if you are using 10 different components of material UI, you'll have 10 different imports in a file.
import CloseIcon from "@mui/icons-material/Close";
import AppBar from "@mui/material/AppBar";
import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import Container from "@mui/material/Container";
import IconButton from "@mui/material/IconButton";
import Paper from "@mui/material/Paper";
import Popover from "@mui/material/Popover";
import TextField from "@mui/material/TextField";
import Toolbar from "@mui/material/Toolbar";
import Typography from "@mui/material/Typography";
It will increase the line of code but it will reduce bundle size.
7. Use forEach Instead of Map
In code, we normally use a map to iterate over an object. Many developers follow this wrong practice. If you only want to iterate over an object you must use forEach. If you want to modify the current object then use a map.
Let's understand this with an example. Let's say we have sizes object as follows:
sizes = {
category: '',
height: '',
width: '',
isSelected: false
}
1. forEach
We want to iterate oversized objects to get all types of size categories into one array. We are not modifying sizes objects but we are iterating oversized objects to get new information.
const allCategory = [];
sizes.forEach((sizeObj) => {
const {
category
} = sizeObj;
if (!allCategory.includes(category)) allCategory.push(category);
});
2. Map
On button click, we want to select all 'custom' category sizes. In this case, we want to modify the sizes object.
const updatedSizes = sizes.map((sizeObj) => {
const {
category
} = sizeObj;
if (category === 'custom') {
const newSizeObj = {
...sizeObj,
isSelected: true,
};
return newSizeObj;
}
return sizeObj;
});
Map returns a new object so in updatedSizes, all 'personal' category sizes will be selected. Following are some of the variable-related practices that you can follow while coding.
- Use const instead of let (if possible)
- Write the meaningful and more understandable variable name
Published at DZone with permission of Hitanshi Mehta. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments