Welcome to the world of React, where creating beautiful and efficient applications has never been easier! With the right set of tips and tricks, you can take your React skills to the next level and create stunning apps that are both user-friendly and optimized for performance.
As you dive deeper into the world of React, it’s important to continually strive for excellence in your code. To assist you in this endeavor, I’d like to share four tips that have been invaluable in helping me write better React code. Whether you’re a seasoned developer or just starting out, I believe you’ll find something of value in these tips. So, let’s dive in and take your React skills to the next level!
1. Return functions from handlers
For those of you who have a background in functional programming, you’ll likely recognize the concept of “currying”. Essentially, it involves setting some parameters for a function ahead of time. By implementing this technique in React, you can streamline your code and make it more efficient. So, let’s delve into the world of currying and see how it can improve your React code.
We have an explicit problem with the boilerplate code below. That technique will help us!
export default function App() {
const [user, setUser] = useState({
firstName: "",
lastName: "",
address: ""
});
// First handler
const handleFirstNameChange = (e) => {
setUser((prev) => ({
...prev,
firstName: e.target.value
}));
};
// Second handler!
const handleLastNameChange = (e) => {
setUser((prev) => ({
...prev,
lastName: e.target.value
}));
};
// Third handler!!!
const handleAddressChange = (e) => {
setUser((prev) => ({
...prev,
address: e.target.value
}));
};
// What if we need one more input? Should we create another handler for it?
return (
<>
<input value={user.fisrtName} onChange={handleFirstNameChange} />
<input value={user.lastName} onChange={handleLastNameChange} />
<input value={user.address} onChange={handleAddressChange} />
</>
);
}
Solution
export default function App() {
const [user, setUser] = useState({
firstName: "",
lastName: "",
address: ""
});
const handleInputChange = (field) => {
return (e) => {
setUser((prev) => ({
...prev,
[field]: e.target.value
}));
};
};
return (
<>
<input value={user.firstName} onChange={handleInputChange("fistName")} />
<input value={user.lastName} onChange={handleInputChange("lastName")} />
<input value={user.address} onChange={handleInputChange("address")} />
{JSON.stringify(user)}
</>
);
}
2. Separate responsibilities
Making a “God” component is a common mistake that developers make. It’s called “God” because it contains many lines of code that are hard to understand and maintain. I strongly recommend dividing components into sets of independent sub-modules.
A typical structure for this would be:
- UI module, responsible for visual representation only.
- Model module, containing only business logic. An example of this is a custom hook.
- Lib module, containing all required utilities for the component.
Here’s a small demo example to help illustrate this concept.
export function ListComponent() {
// Our local state
const [list, setList] = useState([]);
// Handler to load data from the server
const fetchList = async () => {
try {
const resp = await fetch("https://www.example.com/list");
const data = await resp.json();
setList(data);
} catch {
showAlert({ text: "Something went wrong!" });
}
};
// We want to fetch only on mount
useEffect(() => {
fetchList();
}, []);
// Handler responsible for deleting items
const handleDeleteItem = (id) => {
return () => {
try {
fetch(`https://www.example.com/list/${id}`, {
method: "DELETE"
});
setList((prev) => prev.filter((x) => x.id !== id));
} catch {
showAlert({ text: "Something went wrong!" });
}
};
};
// Here we just render our data items
return (
<div className="list-component">
{list.map(({ id, name }) => (
<div key={id} className="list-component__item>">
{/* We want to trim long name with ellipsis */}
{name.slice(0, 30) + (name.length > 30 ? "..." : "")}
<div onClick={handleDeleteItem(id)} className="list-component__icon">
<DeleteIcon />
</div>
</div>
))}
</div>
);
}
We should start by writing our utils that will be used in the model and UI modules.
export async function getList(onSuccess) {
try {
const resp = await fetch("https://www.example.com/list");
const data = await resp.json();
onSuccess(data)
} catch {
showAlert({ text: "Something went wrong!" });
}
}
export async function deleteListItem(id, onSuccess) {
try {
fetch(`https://www.example.com/list/${id}`, {
method: "DELETE"
});
onSuccess()
} catch {
showAlert({ text: "Something went wrong!" });
}
}
export function trimName(name) {
return name.slice(0, 30) + (name.lenght > 30 ? '...' : '')
}
Now we need to implement our business logic. It simply will be a custom hook returning things we need.
export function useList() {
const [list, setList] = useState([]);
const handleDeleteItem = useCallback((id) => {
return () => {
deleteListItem(id, () => {
setList((prev) => prev.filter((x) => x.id !== id));
})
};
}, []);
useEffect(() => {
getList(setList);
}, []);
return useMemo(
() => ({
list,
handleDeleteItem
}),
[list, handleDeleteItem]
);
}
The final step is to write our UI module and then combine it all together.
export function ListComponentItem({ name, onDelete }) {
return (
<div className="list-component__item>">
{trimName(name)}
<div onClick={onDelete} className="list-component__icon">
<DeleteIcon />
</div>
</div>
);
}
export function ListComponent() {
const { list, handleDeleteItem } = useList();
return (
<div className="list-component">
{list.map(({ id, name }) => (
<ListComponentItem
key={id}
name={name}
onDelete={handleDeleteItem(id)}
/>
))}
</div>
);
}
3. Use objects map instead of conditions
When it comes to displaying elements based on specific variables, you can use this powerful tip to simplify your code and make it more intuitive. This approach helps make your components more declarative and easier to understand, while also making it easier to extend their functionality in the future. So, whether you’re working on a complex project or just starting out, implementing this strategy can have a significant impact on the overall quality of your React code.
Problem
💝 Don’t forget to subscribe and stay up-to-date with the latest tips and tricks for Frontend development! With new information being shared regularly, you won’t want to miss out on the opportunity to continue improving your skills and producing high-quality React code.
function Roles({type}) {
let Component = UsualAccount
if (type === 'vip') {
Component = VipAccount
}
if (type === 'admin') {
Component = AdminAccount
}
if (type === 'superadmin') {
Component = SuperAdminAccount
}
return (
<div className='roles'>
<Component />
<RolesStatistics />
</div>
)
}
Solution
const ROLES_MAP = {
'vip': VipAccount,
'usual': UsualAccount,
'admin': AdminAccount,
'superadmin': SuperAdminAccount,
}
function Roles({type}) {
const Component = ROLES_MAP[type]
return (
<div className='roles'>
<Component />
<RolesStatistics />
</div>
)
}
4. Put independent variables outside of React lifecycle
One important concept to keep in mind is separating logic that doesn’t require the React component lifecycle methods from the component itself. This approach can greatly improve the clarity of your code by making dependencies more explicit. As a result, it becomes much easier to read and understand your components, leading to more efficient and effective development. So, make sure to keep this technique in mind as you continue to work with React and improve your code.
Problem
function useItemsList() {
const defaultItems = [10, 20, 30, 40, 50]
const [items, setItems] = useState(defaultItems)
const toggleArrayItem = (arr, val) => {
return arr.includes(val) ? arr.filter(el => el !== val) : [...arr, val];
}
const handleToggleItem = (num) => {
return () => {
setItems(toggleArrayItem(items, num))
}
}
return {
items,
handleToggleItem,
}
}
Solution
const DEFAULT_ITEMS = [
10, 20, 30, 40, 50
]
const toggleArrayItem = (arr, val) => {
return arr.includes(val) ? arr.filter(el => el !== val) : [...arr, val];
}
function useItemsList() {
const [items, setItems] = useState(DEFAULT_ITEMS)
const handleToggleItem = (num) => {
return () => {
setItems(toggleArrayItem(items, num))
}
}
return {
items,
handleToggleItem,
}
}
In conclusion, there are many ways to improve your React code and make it more efficient, effective, and user-friendly. Whether it’s through currying, separating logic from components, using variables to display elements or some other technique, it’s important to stay up-to-date with the latest tips and tricks in the world of Frontend development.
I hope, you found this article useful. If you have any questions or suggestions, please leave comments. Your feedback helps me to become better.
Keep these tips in mind, and never stop striving for excellence in your code.
Don’t forget to subscribe💝
Happy coding!
Add comment