React is incredibly powerful, but mastering it means going beyond the basics and learning some lesser-known tricks to streamline development. Here’s a rundown of my personal favourite 20 React tricks
that can boost your productivity and help you write cleaner, more effective code. Let’s dive straight into examples!
1. Short-Circuit Evaluation for Conditional Rendering
Avoid verbose if
statements for conditional rendering with short-circuit evaluation.
{isLoading && <Spinner />}
This renders the <Spinner />
only when isLoading
is true, keeping your JSX clean.
2. Dynamic Class Names with classnames
Library
The classnames
library makes it easy to conditionally apply classes.
npm install classnames
import classNames from 'classnames';
const buttonClass = classNames({
'btn': true,
'btn-primary': isPrimary,
'btn-secondary': !isPrimary,
});
<button className={buttonClass}>Click Me</button>
3. Memoizing Expensive Calculations with useMemo
If a computation is costly, memoize it so React doesn’t recompute unnecessarily.
const sortedData = useMemo(() => data.sort(), [data]);
This recalculates sortedData
only when data
changes.
4. Debouncing Inputs with useEffect
Avoid constant re-renders by debouncing input changes.
const [value, setValue] = useState('');
const [debouncedValue, setDebouncedValue] = useState('');
useEffect(() => {
const handler = setTimeout(() => setDebouncedValue(value), 500);
return () => clearTimeout(handler);
}, [value]);
<input value={value} onChange={(e) => setValue(e.target.value)} />
5. Custom Hooks for Reusable Logic
Encapsulate logic in a custom hook to reuse it across components.
function useFetch(url) {
const [data, setData] = useState(null);
useEffect(() => {
fetch(url).then(res => res.json()).then(setData);
}, [url]);
return data;
}
const Component = () => {
const data = useFetch('/api/data');
return <div>{data ? JSON.stringify(data) : 'Loading...'}</div>;
};
6. Lazy Loading Components with React.lazy
Optimize loading time by splitting your components.
const LazyComponent = React.lazy(() => import('./LazyComponent'));
function App() {
return (
<React.Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</React.Suspense>
);
}
7. Accessing Previous Props or State with useRef
To access previous state values, use useRef
.
const [count, setCount] = useState(0);
const prevCount = useRef(count);
useEffect(() => {
prevCount.current = count;
}, [count]);
console.log(`Previous: ${prevCount.current}, Current: ${count}`);
8. Avoid Re-renders by Passing Functions to useCallback
If a function doesn’t need to change, memoize it with useCallback
.
const increment = useCallback(() => setCount(count + 1), [count]);
9. Destructuring Props for Cleaner Code
Destructure props right in the function parameters.
const User = ({ name, age }) => (
<div>{name} is {age} years old</div>
);
10. React.Fragment
for Grouping Elements Without Extra Divs
Wrap elements without adding an extra DOM node.
<>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</>
11. Error Boundaries for Catching JavaScript Errors
Catch errors in child components to prevent the whole app from crashing.
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError() {
return { hasError: true };
}
render() {
if (this.state.hasError) return <h1>Something went wrong.</h1>;
return this.props.children;
}
}
12. Using PropTypes
for Prop Validation
Catch bugs early by defining prop types.
import PropTypes from 'prop-types';
function MyComponent({ name }) {
return <div>{name}</div>;
}
MyComponent.propTypes = {
name: PropTypes.string.isRequired,
};
13. State Reducers with useReducer
For complex state logic, useReducer
can be more efficient.
const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case 'increment': return { count: state.count + 1 };
case 'decrement': return { count: state.count - 1 };
default: return state;
}
}
const [state, dispatch] = useReducer(reducer, initialState);
14. useLayoutEffect
for DOM Manipulations
Run effects after DOM updates but before paint.
useLayoutEffect(() => {
console.log("Layout effect");
}, []);
15. Encapsulate State Logic with Context and useContext
Create global state without prop drilling.
const ThemeContext = React.createContext();
function MyComponent() {
const theme = useContext(ThemeContext);
return <div style={{ background: theme }}>Hello!</div>;
}
16. Avoid Inline Function Definitions in JSX
Defining functions inline causes re-renders. Instead, define them outside.
const handleClick = () => console.log("Clicked");
<button onClick={handleClick}>Click Me</button>
17. Use Optional Chaining in JSX for Safe Property Access
Handle null or undefined values gracefully.
<p>{user?.name}</p>
18. Use the key
Prop to Avoid Re-rendering Issues
Always use unique keys when rendering lists.
{items.map(item => (
<div key={item.id}>{item.name}</div>
))}
19. Export Components with Named Exports for Better Import Control
Named exports make importing specific components easier.
export const ComponentA = () => <div>A</div>;
export const ComponentB = () => <div>B</div>;
Then import as needed:
import { ComponentA } from './Components';
20. Reusable Component Patterns: Higher-Order Components (HOCs)
Wrap components with HOCs to add extra logic.
function withLogging(WrappedComponent) {
return function Wrapped(props) {
console.log('Component Rendered');
return <WrappedComponent {...props} />;
};
}
const MyComponentWithLogging = withLogging(MyComponent);
Mastering these tricks will help you write more concise, readable, and efficient React code! Happy coding!
Top comments (2)
In 12 what is the benefit of prop types over using Interfaces. Haven't seen this before so I was wondering.
Personally I prefer TypeScript interfaces over the older PropTypes.