Tip/Mistake 1: Use export const
Instead of export default
Many developers use export default
because it seems obvious, but it has drawbacks:
- The function name is fixed and hard to rename during import.
- In large codebases, this can lead to errors.
Advantage of export const
:
Each component has a clear, unique name, which makes it easier to find and debug.
Example with export default
:
export default function getData() {
return fetch('/api/data');
}
Problem:
You must use the exact name when importing:
import getData from './getData'; // Cannot rename without extra work
Example with export const
:
export const getData = () => fetch('/api/data');
Advantage:
You can import it by name and even rename it if needed:
import { getData } from './getData';
Tip/Mistake 2: Using useFormStatus
to Improve UX for Post Creation
Manually handling form submission state with useState
can add extra work. React’s useFormStatus
hook simplifies tracking form submission without extra code.
Example:
import { useFormStatus } from 'react-dom';
function SubmitButton() {
const { pending } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? 'Creating...' : 'Publish'}
</button>
);
}
function CreatePostForm() {
return (
<form action="/create-post" method="POST">
<label>
Title:
<input type="text" name="title" required />
</label>
<label>
Content:
<textarea name="content" required />
</label>
<SubmitButton />
</form>
);
}
Benefits:
- Simplifies form submission state management
- Integrates automatically with React form actions
- Keeps your code clean without extra local state
Tip/Mistake 3: Tailwind CSS Classes You Didn’t Know (sr-only and Others)
Many developers overlook useful Tailwind CSS classes that improve accessibility and UI ease.
Example: Using sr-only
to Hide Content from the UI but Keep It for Screen Readers
export default function AccessibleInfo() {
return (
<div className="relative p-4">
<div className="sr-only">
This button opens a modal with detailed information.
</div>
<button
onClick={() => alert('Modal opened')}
className="bg-blue-500 text-white px-4 py-2 rounded-lg hover:bg-blue-600 transition"
>
Open Modal
</button>
</div>
);
}
If you want the element to be visible on larger screens, use not-sr-only
:
export default function AccessibleInfo() {
return (
<div className="relative p-4">
<div className="sr-only sm:not-sr-only">
This button opens a modal with detailed information.
</div>
<button
onClick={() => alert('Modal opened')}
className="bg-blue-500 text-white px-4 py-2 rounded-lg hover:bg-blue-600 transition"
>
Open Modal
</button>
</div>
);
}
Text Selection Control
- select-none: Prevents text selection.
- select-text: Allows text selection.
- select-all: Selects all text on click.
- select-auto: Uses the browser's default behavior.
<div class="select-none">This text cannot be selected.</div>
<div class="select-text">This text can be selected.</div>
<div class="select-all">Click to select all text.</div>
<div class="select-auto">Default text selection behavior.</div>
Scrolling Settings
- scroll-auto: Normal scrolling.
- scroll-smooth: Smooth scrolling.
<div class="scroll-smooth h-40 overflow-y-scroll">
<!-- Content here -->
</div>
Hiding the Default File Input
<label class="bg-blue-500 text-white px-4 py-2 rounded cursor-pointer">
Choose a file
<input type="file" class="file:hidden" />
</label>
Tip/Mistake 4: Using Component Libraries vs. Native Code
Many developers start projects by using ready-made UI components from popular libraries. This is convenient because these libraries offer many features "out of the box." However, as your app grows, importing an entire library like react-select
can add about 95 kB to your final bundle, which slows down your app.
Rule: Use ready-made libraries for a quick start, but switch to native solutions when your project scales.
Example Using react-select
:
import React from 'react';
import Select from 'react-select';
const options = [
{ value: 'chocolate', label: 'Chocolate' },
{ value: 'strawberry', label: 'Strawberry' },
{ value: 'vanilla', label: 'Vanilla' },
];
const App = () => (
<Select options={options} />
);
export default App;
Example Using Native <select>
:
import React, { useState } from 'react';
import './App.css'; // Animation styles
const options = [
{ value: 'chocolate', label: 'Chocolate' },
{ value: 'strawberry', label: 'Strawberry' },
{ value: 'vanilla', label: 'Vanilla' },
];
const App = () => {
const [selectedValue, setSelectedValue] = useState('');
return (
<div>
<select
className="custom-select"
value={selectedValue}
onChange={(e) => setSelectedValue(e.target.value)}
>
<option disabled value="">
Choose an option
</option>
{options.map((option) => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
</div>
);
};
export default App;
Tip/Mistake 5: The Depcheck Tool
Depcheck is a tool that checks your code to find dependencies listed in your package.json
that are not actually used in your project. This helps reduce your app’s size by removing unnecessary packages.
How to Use Depcheck:
-
Install depcheck:
yarn global add depcheck
-
Imagine you have this
package.json
:
{ "dependencies": { "react": "^18.2.0", "lodash": "^4.17.21", "moment": "^2.29.4" } }
-
Run depcheck:
yarn depcheck
Expected Output (if lodash and moment are unused):
Unused dependencies:
* lodash
* moment
Unused devDependencies:
* none
Missing dependencies:
* none
Full video (please like and subscribe) - https://youtu.be/KCh6ztZBJoY
Tip/Mistake 6: Optimizing Enums
TypeScript’s enum
is useful for grouping values, but it has drawbacks:
- It increases the bundle size due to extra reverse mapping code.
- It can cause issues with tree shaking.
- It may introduce bugs with reverse mapping.
Solution: Replace enum
with an object using the as const
modifier.
Example with enum
:
export enum EnumSTATUSES {
SUCCESS = 'SUCCESS',
ERROR = 'ERROR',
PENDING = 'PENDING'
}
Example with as const
:
export const EnumSTATUSES = {
SUCCESS: 'SUCCESS',
ERROR: 'ERROR',
PENDING: 'PENDING'
} as const;
type EnumStatusesType = (typeof EnumSTATUSES)[keyof typeof EnumSTATUSES];
Using as const
makes the object immutable and lets TypeScript infer exact types (e.g., 'SUCCESS' | 'ERROR' | 'PENDING'
).
Tip/Mistake 7: Choosing Between useEffect
and useLayoutEffect
Using the wrong hook can cause visual glitches and performance issues.
The Difference:
- useEffect: Runs after the component renders; changes are visible.
- useLayoutEffect: Runs synchronously after rendering but before the browser updates the screen.
Example with useEffect
:
import React, { useState, useEffect, useRef } from 'react';
function ResizableBox() {
const [width, setWidth] = useState(200);
const boxRef = useRef(null);
useEffect(() => {
if (boxRef.current) {
const newWidth = boxRef.current.offsetWidth;
setWidth(newWidth);
}
}, []);
return (
<div>
<div
ref={boxRef}
style={{ width: '100%', border: '1px solid black', height: '50px' }}
>
I change my width!
</div>
<p>Current width: {width}px</p>
</div>
);
}
export default ResizableBox;
Example with useLayoutEffect
:
import React, { useState, useLayoutEffect, useRef } from 'react';
function ResizableBox() {
const [width, setWidth] = useState(200);
const boxRef = useRef(null);
useLayoutEffect(() => {
if (boxRef.current) {
const newWidth = boxRef.current.offsetWidth;
setWidth(newWidth);
}
}, []);
return (
<div>
<div
ref={boxRef}
style={{ width: '100%', border: '1px solid black', height: '50px' }}
>
I change my width!
</div>
<p>Current width: {width}px</p>
</div>
);
}
export default ResizableBox;
Using useLayoutEffect
ensures changes occur before the user sees the update, avoiding visual glitches.
Tip/Mistake 8: The Knip Tool
Knip is an advanced static analysis tool that checks not only your dependencies but also unused CSS classes, functions, variables, and files.
How to Use Knip:
-
Install Knip:
yarn add knip
-
Example CSS file:
/* App.css */ .used-class { color: blue; } .unused-class { font-size: 20px; /* This class is not used */ }
-
Example JavaScript file:
// src/App.js import React from 'react'; import _ from 'lodash'; // Unused function const unusedFunction = () => { console.log('This function is never called'); }; const App = () => { return ( <div className="used-class"> {_.join(['Hello', 'World'], ' ')} </div> ); }; export default App;
-
Example of an unused file:
// src/oldFile.js export const oldVariable = 'This variable is never used';
-
Run Knip:
yarn knip
Knip will analyze your project and list unused CSS classes, exports, and files. Use it regularly to keep your code clean.
Tip/Mistake 9: Missing key
When Rendering Lists
Forgetting the key
attribute when rendering lists can lead to performance issues and unpredictable component behavior.
Why key
is Important:
- Stability: If the order of items changes, using indexes can cause loss of state.
- Performance: React updates only the changed elements when unique keys are provided.
Bad Example (No key
):
const items = ['Apple', 'Banana', 'Cherry'];
function BadList() {
return (
<ul>
{items.map((item) => (
<li>{item}</li> // Missing key
))}
</ul>
);
}
Good Example (Using Unique key
):
const items = [
{ id: 1, name: 'Apple' },
{ id: 2, name: 'Banana' },
{ id: 3, name: 'Cherry' },
];
function BetterList() {
return (
<ul>
{items.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
}
Using unique IDs instead of indexes ensures stable and efficient list rendering.
Tip/Mistake 10: Use a Seeder on the Back-End
Using a Seeder is an efficient way to populate your database with initial or test data. This speeds up development and testing.
Example Seeder for an Online Store:
const products = [
{
name: 'Laptop',
description: 'A powerful laptop',
price: 899.99,
stock: 50
},
{
name: 'Smartphone',
description: 'A top-tier smartphone',
price: 799.99,
stock: 30
},
{
name: 'Headphones',
description: 'Premium wireless headphones',
price: 349.99,
stock: 20
}
];
async function seedDatabase() {
try {
// Make sure to use the correct model and method for creating entries
await Product.bulkCreate(products);
console.log('Database seeded successfully');
} catch (error) {
console.error('Error seeding the database:', error);
}
}
seedDatabase();
Explanation:
- Products Array: Contains objects with product details (name, description, price, stock).
-
seedDatabase Function: Populates the database using the
bulkCreate
method. On success, it logs a message; on error, it logs the error.
Full video (please like and subscribe) - https://youtu.be/KCh6ztZBJoY
Top comments (0)