101 React Tips & Tricks
Written by Ndeye Fatou Diop
© 2024 Ndeye Fatou Diop.
All rights reserved. The author has taken care in preparation of this
book, but makes no expressed or implied warranty of any kind, and
assumes no responsibility for errors or omissions. No liability is
assumed for incidental or consequential damages in with or arising
out of the use of the information contained herein.
This book is self-published.
101 React Tips & Tricks 1 / 140
THIS BOOK BELONGS TO
This digital workbook is made available to you for
personal use only. You may use, reuse, print and reprint
the pages of this book as many times as you like for
your own personal reference, but please do not share any
part of this book (either digitally or in print) with others.
Instead, you can direct them to
https://ndeyefatoudiop.gumroad.com/l/101-react-tips-
and-tricks, where I offer this book for free.
Thank you!
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 2 / 140
INTRODUCTION
I have been working professionally with React since
2019.
In this article, I share the 101 best tips & tricks I learned
over the years.
This guide assumes a basic familiarity with React
and an understanding of the terms props ,
state , context , etc.
I tried to use Vanilla JS for most of the examples to
keep things simple. If you're using TypeScript, you
can easily adapt the code.
The code is not production-ready. Use at your own
discretion.
If you have any questions or suggestions, feel free
to reach out to me on Twitter @_ndeyefatoudiop.
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 3 / 140
TABLE OF CONTENTS
Category #1: Components organization 🧹
1. Use self-closing tags to keep your code
compact
2. Prefer fragments over DOM nodes (e.g.,
div, span, etc.) to group elements
3. Use React fragment shorthand <></>
(except if you need to set a key)
4. Prefer spreading props over accessing each
one individually
5. When setting default values for props, do it
while destructuring them
6. Drop curly braces when passing string
type props.
7. Ensure that value is a boolean before
using value && <Component
{...props}/> to prevent displaying
unexpected values on the screen.
8. Use functions (inline or not) to avoid
polluting your scope with intermediate
variables
9. Use curried functions to reuse logic (and
properly memoize callback functions)
10. Move data that doesn't rely on the
component props/state outside of it for
cleaner (and more efficient) code
11. When storing the selected item from a list,
store the item ID rather than the entire item
12. If you're frequently checking a prop's value
before doing something, introduce a new
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 4 / 140
component
13. Use the CSS :empty pseudo-class to
hide elements with no children
14. Group all the state and context at the top
of the component
Category #2: Effective Design Patterns &
Techniques 🛠
15. Leverage the children props for cleaner
code (and performance benefits)
16. Build composable code with compound
components
17. Make your code more extensible with
render functions or component
functions props
18. When dealing with different cases, use
value === case && <Component /> to
avoid holding onto old state
19. Always use error boundaries
Category #3: Keys & Refs 🗝
20. Use crypto.randomUUID or
Math.random to generate keys
21. Make sure your list items IDs are stable
(i.e., they don't change between renders)
22. Strategically use the key attribute to
trigger component re-renders
23. Use a ref callback function for
tasks such as monitoring size changes and
managing multiple node elements.
Category #4: Organizing React code 🧩
24. Colocate React components with their
assets (e.g., styles, images, etc.)
25. Limit your component file size
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 5 / 140
26. Limit the number of return statements in
your functional component file
27. Prefer named exports over default exports
Category #5: Efficient State Management 🚦
28. Never create a state for a value that can
be derived from other state or props
29. Keep the state at the lowest level
necessary to minimize re-renders
30. Clarify the distinction between the initial
state and the current state
31. Update state based on the previous state,
especially when memoizing with
useCallback
32. Use functions in useState for lazy
initialization and performance gains, as they
are invoked only once.
33. Use react context for broadly needed,
static state to prevent prop drilling
34. React Context: Split your context into
parts that change frequently and those that
change infrequently to enhance app
performance
35. React Context: Introduce a Provider
component when the value computation is
not straightforward
36. Consider using the useReducer hook
as a lightweight state management solution
37. Simplify state updates with useImmer
or useImmerReducer
38. Use Redux (or another state management
solution) for complex client-side state
accessed across multiple components
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 6 / 140
39. Redux: Use Redux DevTools to debug your
state
Category #6: React Code Optimization 🚀
40. Prevent unnecessary re-renders with
memo
41. Specify an equality function with memo to
instruct React on how to compare the props.
42. Prefer named functions over arrow
functions when declaring a memoized
component
43. Cache expensive computations or
preserve references with useMemo
44. Use useCallback to memoize
functions
45. Memoize callbacks or values returned
from utility hooks to avoid performance
issues
46. Leverage lazy loading and Suspense to
make your apps load faster
47. Throttle your network to simulate a slow
network
48. Use react-window or react-
virtuoso to efficiently render lists
Category #7: Debugging React code 🐞
49. Use StrictMode to catch bugs in your
components before deploying them to
production
50. Install the React Developer Tools browser
extension to view/edit your components and
detect performance issues
51. React DevTools Components: Highlight
components that render to identify potential
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 7 / 140
issues
52. Leverage useDebugValue in your
custom hooks for better visibility in React
DevTools
53. Use the why-did-you-render library
to track component rendering and identify
potential performance bottlenecks
54. Hide logs during the second render in
Strict Mode
Category #8: Testing React code 🧪
55. Use React Testing Library to test
your React components effectively
56. React Testing Library: Use testing
playground to effortlessly create queries
57. Conduct end-to-end tests with Cypress
or Playwright
58. Use MSW to mock network requests in
your tests
Category #9: React hooks 🎣
59. Make sure you perform any required
cleanup in your useEffect hooks
60. Use refs for accessing DOM elements
61. Use refs to preserve values across re-
renders
62. Prefer named functions over arrow
functions within hooks such as useEffect
to easily find them in React Dev Tools
63. Encapsulate logic with custom hooks
64. Prefer functions over custom hooks
65. Prevent visual UI glitches by using the
useLayoutEffect hook
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 8 / 140
66. Generate unique IDs for accessibility
attributes with the useId hook
67. Use the useSyncExternalStore to
subscribe to an external store
68. Use the useDeferredValue hook to
display the previous query results until the
new results become available
Category #10: Must-known React Libraries/Tools 🧰
69. Incorporate routing into your app with
react-router
70. Implement first-class data fetching in
your app with swr or React Query
71. Simplify form state management with
libraries like formik , React Hook Form ,
or TanStack Form
72. Internationalize your app using
Format.js, Lingui, or react-
i18next.
73. Effortlessly create impressive animations
with framer-motion
74. Tired of re-inventing the wheel with
custom hooks? Check out
https://usehooks.com/
75. Streamline app development by
leveraging UI libraries like Shadcdn or
Headless UI
76. Check your website's accessibility with
the axe-core-npm library
77. Refactor React code effortlessly with
react-codemod
78. Transform your app into a Progressive Web
Application (PWA) using vite-pwa
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 9 / 140
Category #11: React & Visual Studio Code 🛠
79. Enhance your productivity with the Simple
React Snippets snippets extension
80. Set editor.stickyScroll.enabled
to true to quickly locate the current
component
81. Simplify refactoring with extensions like
VSCode Glean or VSCode React Refactor
Category #12: React & TypeScript 🚀
82. Use ReactNode instead of
JSX.Element | null | undefined |
... to keep your code more compact
83. Simplify the typing of components
expecting children props with
PropsWithChildren
84. Access element props efficiently with
ComponentProps ,
ComponentPropsWithoutRef ,…
85. Leverage types like
MouseEventHandler ,
FocusEventHandler and others for
concise typings
86. Specify types explicitly in useState,
useRef, etc., when the type can't be or
shouldn't be inferred from the initial value
87. Leverage the Record type for cleaner
and more extensible code
88. Use the as const trick to accurately
type your hook return values
89. Redux: Ensure proper typing by referring
to https://react-redux.js.org/using-react-
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 10 / 140
redux/usage-with-typescript to correctly type
your Redux state and helpers
90. Simplify your types with
ComponentType
91. Make your code more reusable with
TypeScript generics
92. Ensure precise typing with the NoInfer
utility type
93. Effortlessly type refs with the
ElementRef type helper
Category #13: Miscellaneous Tips 🎉
94. Boost your code's quality and safety with
eslint-plugin-react and Prettier.
95. Log and monitor your app with tools like
Sentry or Grafana Cloud Frontend
Observability.
96. Start coding quickly with online IDEs like
Code Sandbox or Stackblitz
97. Looking for advanced react skills? Check
out these books 👇
98. Prepping React interviews? Check reactjs-
interview-questions
99. Learn React best practices from experts
like Nadia, Dan, Josh, Kent, etc.
100. Stay updated with the React ecosystem
by subscribing to newsletters like This Week
In React or ui.dev
101. Engage with the React community on
platforms like r/reactjs
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 11 / 140
CATEGORY #1: COMPONENTS
ORGANIZATION 🧹
➥ Tip # 1. Use self-closing tags to keep your
code compact
// ❌Bad: too verbose
<MyComponent></MyComponent>
// ✅Good
<MyComponent/>
➥ Tip # 2. Prefer fragments over DOM nodes
(e.g., div, span, etc.) to group elements
In React, each component must return a single element.
Instead of wrapping multiple elements in a <div> or
<span> , use <Fragment> to keep your DOM neat and
tidy.
❌ Bad: Using div clutters your DOM and may require
more CSS code.
function Dashboard() {
return (
<div>
<Header />
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 12 / 140
<Main />
</div>
);
}
✅ Good: <Fragment> wraps elements without
affecting the DOM structure.
function Dashboard() {
return (
<Fragment>
<Header />
<Main />
</Fragment>
);
}
➥ Tip # 3. Use React fragment shorthand <>
</> (except if you need to set a key)
❌ Bad: The code below is unnecessarily verbose.
<Fragment>
<FirstChild />
<SecondChild />
</Fragment>
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 13 / 140
✅ Good: Unless, you need a key , <></> is more
concise.
<>
<FirstChild />
<SecondChild />
</>;
// Using a `Fragment` here is required
because of the key.
function List({ users }) {
return (
<div>
{users.map((user) => (
<Fragment key={user.id}>
<span>{user.name}</span>
<span>{user.occupation}</span>
</Fragment>
))}
</div>
);
}
➥ Tip # 4. Prefer spreading props over
accessing each one individually
❌ Bad: The code below is harder to read (especially at
scale).
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 14 / 140
// We do `props…` all over the code.
function TodoList(props) {
return (
<div>
{props.todos.map((todo) => (
<div key={todo}>
<button
onClick={() =>
props.onSelectTodo(todo)}
style={{
backgroundColor: todo ===
props.selectedTodo ? "gold" : undefined,
}}
>
<span>{todo}</span>
</button>
</div>
))}
</div>
);
}
✅ Good: The code below is more concise.
function TodoList({ todos, selectedTodo,
onSelectTodo }) {
return (
<div>
{todos.map((todo) => (
<div key={todo}>
<button
onClick={() =>
onSelectTodo(todo)}
style={{
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 15 / 140
backgroundColor: todo ===
selectedTodo ? "gold" : undefined,
}}
>
<span>{todo}</span>
</button>
</div>
))}
</div>
);
}
➥ Tip # 5. When setting default values for props,
do it while destructuring them
❌ Bad: You may need to define the defaults in multiple
places and introduce new variables.
function Button({ onClick, text, small,
colorScheme }) {
let scheme = colorScheme || "light";
let isSmall = small || false;
return (
<button
onClick={onClick}
style={{
color: scheme === "dark" ? "white"
: "black",
fontSize: isSmall ? "12px" :
"16px",
}}
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 16 / 140
>
{text ?? "Click here"}
</button>
);
}
✅ Good: You can set all your defaults in one place at the
top. This makes it easy for someone to locate them.
function Button({
onClick,
text = "Click here",
small = false,
colorScheme = "light",
}) {
return (
<button
onClick={onClick}
style={{
color: colorScheme === "dark" ?
"white" : "black",
fontSize: small ? "12px" : "16px",
}}
>
{text}
</button>
);
}
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 17 / 140
➥ Tip # 6. Drop curly braces when passing
string type props.
// ❌Bad: curly braces are not needed
<Button text={"Click me"} colorScheme=
{"dark"} />
// ✅Good
<Button text="Click me" colorScheme="dark"
/>
➥ Tip # 7. Ensure that value is a boolean
before using value && <Component
{...props}/> to prevent displaying
unexpected values on the screen.
❌ Bad: When the list is empty, 0 will be printed on the
screen.
export function ListWrapper({ items,
selectedItem, setSelectedItem }) {
return (
<div className="list">
{items.length && ( // `0` if the list
is empty
<List
items={items}
onSelectItem={setSelectedItem}
selectedItem={selectedItem}
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 18 / 140
/>
)}
</div>
);
}
✅ Good: Nothing will be printed on the screen when
there are no items.
export function ListWrapper({ items,
selectedItem, setSelectedItem }) {
return (
<div className="list">
{items.length > 0 && (
<List
items={items}
onSelectItem={setSelectedItem}
selectedItem={selectedItem}
/>
)}
</div>
);
}
➥ Tip # 8. Use functions (inline or not) to avoid
polluting your scope with intermediate variables
❌ Bad: The variables gradeSum and gradeCount
are cluttering the component's scope.
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 19 / 140
function Grade({ grades }) {
if (grades.length === 0) {
return <>No grades available.</>;
}
let gradeSum = 0;
let gradeCount = 0;
grades.forEach((grade) => {
gradeCount++;
gradeSum += grade;
});
const averageGrade = gradeSum /
gradeCount;
return <>Average Grade: {averageGrade}
</>;
}
✅ Good: The variables gradeSum and
gradeCount are scoped within
computeAverageGrade function.
function Grade({ grades }) {
if (grades.length === 0) {
return <>No grades available.</>;
}
const computeAverageGrade = () => {
let gradeSum = 0;
let gradeCount = 0;
grades.forEach((grade) => {
gradeCount++;
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 20 / 140
gradeSum += grade;
});
return gradeSum / gradeCount;
};
return <>Average Grade:
{computeAverageGrade()}</>;
}
💡 Note: you can also define a function
computeAverageGrade outside the component
and call it inside it.
➥ Tip # 9. Use curried functions to reuse logic
(and properly memoize callback functions)
❌ Bad: The logic for updating a field is very repetitive.
function Form() {
const [{ name, email }, setFormState] =
useState({
name: "",
email: "",
});
return (
<>
<h1>Class Registration Form</h1>
<form>
<label>
Name:{" "}
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 21 / 140
<input
type="text"
value={name}
onChange={(evt) =>
setFormState((formState) =>
({
...formState,
name: evt.target.value,
}))
}
/>
</label>
<label>
Email:{" "}
<input
type="email"
value={email}
onChange={(evt) =>
setFormState((formState) =>
({
...formState,
email: evt.target.value,
}))
}
/>
</label>
</form>
</>
);
}
✅ Good: Introduce
createFormValueChangeHandler that returns the
correct handler for each field.
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 22 / 140
Note: This trick is especially nice if you have the
ESLint rule jsx-no-bind turned on. You can just
wrap the curried function inside useCallback
and "Voilà!".
function Form() {
const [{ name, email }, setFormState] =
useState({
name: "",
email: "",
});
const createFormValueChangeHandler =
(field) => {
return (event) => {
setFormState((formState) => ({
...formState,
[field]: event.target.value,
}));
};
};
return (
<>
<h1>Class Registration Form</h1>
<form>
<label>
Name:{" "}
<input
type="text"
value={name}
onChange=
{createFormValueChangeHandler("name")}
/>
</label>
<label>
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 23 / 140
Email:{" "}
<input
type="email"
value={email}
onChange=
{createFormValueChangeHandler("email")}
/>
</label>
</form>
</>
);
}
➥ Tip # 10. Move data that doesn't rely on the
component props/state outside of it for cleaner
(and more e cient) code
❌ Bad: OPTIONS and renderOption don't need to
be inside the component because they don't depend on
any props or state.
Also, keeping them inside means we get new object
references every time the component renders. If we were
to pass renderOption to a child component wrapped
in memo , it would break the memoization.
function CoursesSelector() {
const OPTIONS = ["Maths", "Literature",
"History"];
const renderOption = (option: string) =>
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 24 / 140
{
return <option>{option}</option>;
};
return (
<select>
{OPTIONS.map((opt) => (
<Fragment key={opt}>
{renderOption(opt)}</Fragment>
))}
</select>
);
}
✅ Good: Move them out of the component to keep the
component clean and references stable.
const OPTIONS = ["Maths", "Literature",
"History"];
const renderOption = (option: string) => {
return <option>{option}</option>;
};
function CoursesSelector() {
return (
<select>
{OPTIONS.map((opt) => (
<Fragment key={opt}>
{renderOption(opt)}</Fragment>
))}
</select>
);
}
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 25 / 140
💡 Note: In this example, you can simplify further
by using the option element inline.
const OPTIONS = ["Maths", "Literature",
"History"];
function CoursesSelector() {
return (
<select>
{OPTIONS.map((opt) => (
<option key={opt}>{opt}</option>
))}
</select>
);
}
➥ Tip # 11. When storing the selected item from
a list, store the item ID rather than the entire
item
❌ Bad: If an item is selected but then it changes (i.e.,
we receive a completely new object reference for the
same ID), or if the item is no longer present in the list,
selectedItem will either retain an outdated value or
become incorrect.
function ListWrapper({ items }) {
// We are referencing the entire item
const [selectedItem, setSelectedItem] =
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 26 / 140
useState<Item | undefined>();
return (
<>
{selectedItem != null && <div>
{selectedItem.name}</div>}
<List
items={items}
selectedItem={selectedItem}
onSelectItem={setSelectedItem}
/>
</>
);
}
✅ Good: We store the selected item by its ID (which
should be stable). This ensures that even if the item is
removed from the list or one of its properties changed,
the UI should be correct.
function ListWrapper({ items }) {
const [selectedItemId, setSelectedItemId]
= useState<number | undefined>();
// We derive the selected item from the
list
const selectedItem = items.find((item) =>
item.id === selectedItemId);
return (
<>
{selectedItem != null && <div>
{selectedItem.name}</div>}
<List
items={items}
selectedItemId={selectedItemId}
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 27 / 140
onSelectItem={setSelectedItemId}
/>
</>
);
}
➥ Tip # 12. If you're frequently checking a prop's
value before doing something, introduce a new
component
❌ Bad: The code is cluttered because of all the user
== null checks.
Here, we can't return early because of the rules of
hooks.
function Posts({ user }) {
// Due to the rules of hooks, `posts` and
`handlePostSelect` must be declared before
the `if` statement.
const posts = useMemo(() => {
if (user == null) {
return [];
}
return getUserPosts(user.id);
}, [user]);
const handlePostSelect = useCallback(
(postId) => {
if (user == null) {
return;
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 28 / 140
}
// TODO: Do something
},
[user]
);
if (user == null) {
return null;
}
return (
<div>
{posts.map((post) => (
<button key={post.id} onClick={()
=> handlePostSelect(post.id)}>
{post.title}
</button>
))}
</div>
);
}
✅ Good: We introduce a new component, UserPosts ,
that takes a defined user and is much cleaner.
function Posts({ user }) {
if (user == null) {
return null;
}
return <UserPosts user={user} />;
}
function UserPosts({ user }) {
const posts = useMemo(() =>
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 29 / 140
getUserPosts(user.id), [user.id]);
const handlePostSelect = useCallback(
(postId) => {
// TODO: Do something
},
[user]
);
return (
<div>
{posts.map((post) => (
<button key={post.id} onClick={()
=> handlePostSelect(post.id)}>
{post.title}
</button>
))}
</div>
);
}
➥ Tip # 13. Use the CSS :empty pseudo-class
to hide elements with no children
In the example below 👇
, a wrapper takes children and
adds a red border around them.
function PostWrapper({ children }) {
return <div className="posts-wrapper">
{children}</div>;
}
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 30 / 140
.posts-wrapper {
border: solid 1px red;
}
❌ Problem: The border remains visible on the screen
even if the children are empty (i.e., equal to null ,
undefined , etc.).
✅ Solution: Use the :empty CSS pseudo-class to
ensure the wrapper is not displayed when it's empty.
.posts-wrapper:empty {
display: none;
}
➥ Tip # 14. Group all the state and context at the
top of the component
When all the state and context are located at the top, it
is easy to spot what can trigger a component re-render.
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 31 / 140
❌ Bad: State and context are scattered, making it hard
to track.
function App() {
const [email, setEmail] = useState("");
const onEmailChange = (event) => {
setEmail(event.target.value);
};
const [password, setPassword] =
useState("");
const onPasswordChange = (event) => {
setPassword(event.target.value);
};
const theme = useContext(ThemeContext);
return (
<div className={`App ${theme}`}>
<h1>Welcome</h1>
<p>
Email: <input type="email" value=
{email} onChange={onEmailChange} />
</p>
<p>
Password:{" "}
<input type="password" value=
{password} onChange={onPasswordChange} />
</p>
</div>
);
}
✅ Good: All state and context are grouped at the top,
making it easy to spot.
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 32 / 140
function App() {
const theme = useContext(ThemeContext);
const [email, setEmail] = useState("");
const [password, setPassword] =
useState("");
const onEmailChange = (event) => {
setEmail(event.target.value);
};
const onPasswordChange = (event) => {
setPassword(event.target.value);
};
return (
<div className={`App ${theme}`}>
<h1>Welcome</h1>
<p>
Email: <input type="email" value=
{email} onChange={onEmailChange} />
</p>
<p>
Password:{" "}
<input type="password" value=
{password} onChange={onPasswordChange} />
</p>
</div>
);
}
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 33 / 140
CATEGORY #2: EFFECTIVE DESIGN
PATTERNS & TECHNIQUES 🛠
➥ Tip # 15. Leverage the children props for
cleaner code (and performance benefits)
Using the children props has several benefits:
Benefit #1: You can avoid prop drilling by passing
props directly to children components instead of
routing them through the parent.
Benefit #2: Your code is more extensible since you
can easily modify children without changing the
parent component.
Benefit #3: You can use this trick to avoid re-
rendering "slow" components (see in the example
below 👇
).
❌ Bad: MyVerySlowComponent renders whenever
Dashboard renders, which happens every time the
current time updates.
💡 You can see it in action here, where I use React
Developer Tool's profiler.
function App() {
// Some other logic…
return <Dashboard />;
}
function Dashboard() {
const [currentTime, setCurrentTime] =
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 34 / 140
useState(new Date());
useEffect(() => {
const intervalId = setInterval(() => {
setCurrentTime(new Date());
}, 1_000);
return () => clearInterval(intervalId);
}, []);
return (
<>
<h1>{currentTime.toTimeString()}</h1>
<MyVerySlowComponent /> {/* Renders
whenever `Dashboard` renders */}
</>
);
}
✅ Good: MyVerySlowComponent doesn't render
when Dashboard renders.
function App() {
return (
<Dashboard>
<MyVerySlowComponent />
</Dashboard>
);
}
function Dashboard({ children }) {
const [currentTime, setCurrentTime] =
useState(new Date());
useEffect(() => {
const intervalId = setInterval(() => {
setCurrentTime(new Date());
}, 1_000);
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 35 / 140
return () => clearInterval(intervalId);
}, []);
return (
<>
<h1>{currentTime.toTimeString()}</h1>
{children}
</>
);
}
💡 You can see it in action here.
➥ Tip # 16. Build composable code with
compound components
Think of compound components as Lego blocks.
You piece them together to create a customized UI.
These components work exceptionally well when
creating libraries, resulting in both expressive and
highly extendable code.
You can explore this pattern further here 👉
Compound Pattern
Example from reach.ui (Menu, MenuButton, MenuList,
MenuLink are compound components)
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 36 / 140
<Menu>
<MenuButton>
Actions <span aria-hidden>▾</span>
</MenuButton>
<MenuList>
<MenuItem onSelect={() =>
alert("Download")}>Download</MenuItem>
<MenuItem onSelect={() =>
alert("Copy")}>Create a Copy</MenuItem>
<MenuLink as="a"
href="https://reacttraining.com/workshops/"
>
Attend a Workshop
</MenuLink>
</MenuList>
</Menu>
➥ Tip # 17. Make your code more extensible with
render functions or component
functions props
Let's say we want to display various lists, such as
messages, profiles, or posts, and each one should be
sortable.
To achieve this, we introduce a List component for
reuse. There are two ways we can go around this:
❌ Bad: Option 1 List handles rendering each item
and how they are sorted. This is problematic as it
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 37 / 140
violates the Open Closed Principle. Whenever a new item
type is added, this code will be modified.
✅ Good: Option 2 List takes render functions or
component functions, invoking them only when needed.
You can find an example in this sandbox below 👇:
🏖 https://codesandbox.io/p/sandbox/nice-
feistel-7yd768
➥ Tip # 18. When dealing with di erent cases,
use value === case && <Component />
to avoid holding onto old state
❌ Problem: In the sandbox below, the counter doesn't
reset when switching between Posts and Snippets .
This happens because when rendering the same
component, its state persists across type changes.
🏖 https://codesandbox.io/p/sandbox/counter-
zrl9pf
✅ Solution: Render a component based on the
selectedType or use a key to force a reset when the
type changes.
function App() {
const [selectedType, setSelectedType] =
useState < ResourceType > "posts";
return (
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 38 / 140
<>
<Navbar selectedType={selectedType}
onSelectType={setSelectedType} />
{selectedType === "posts" &&
<Resource type="posts" />}
{selectedType === "snippets" &&
<Resource type="snippets" />}
</>
);
}
// We use the `selectedType` as a key
function App() {
const [selectedType, setSelectedType] =
useState < ResourceType > "posts";
return (
<>
<Navbar selectedType={selectedType}
onSelectType={setSelectedType} />
<Resource type={selectedType} key=
{selectedType} />
</>
);
}
➥ Tip # 19. Always use error boundaries
By default, if your application encounters an error during
rendering, the entire UI crashes . 💥
To prevent this, use error boundaries to:
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 39 / 140
Keep parts of your app functional even if an error
occurs.
Display user-friendly error messages and
optionally track errors.
💡 Tip: you can use the react-error-boundary
library.
CATEGORY #3: KEYS & REFS 🗝
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 40 / 140
➥ Tip # 20. Use crypto.randomUUID or
Math.random to generate keys
JSX elements inside a map() call always need keys.
Suppose your elements don't already have keys. In that
case, you can generate unique IDs using
crypto.randomUUID , Math.random , or the uuid
library.
Note: Beware that crypto.randomUUID is not
defined in older browsers.
➥ Tip # 21. Make sure your list items IDs are
stable (i.e., they don't change between renders)
As much as possible, keys/IDs should be stable.
Otherwise, React may re-render some components
uselessly, or selections will no longer be valid, like in the
example below.
❌ Bad: selectedQuoteId changes whenever App
renders, so there is never a valid selection.
function App() {
const [quotes, setQuotes] = useState([]);
const [selectedQuoteId,
setSelectedQuoteId] = useState(undefined);
// Fetch quotes
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 41 / 140
useEffect(() => {
const loadQuotes = () =>
fetchQuotes().then((result) => {
setQuotes(result);
});
loadQuotes();
}, []);
// Add ids: this is bad!!!
const quotesWithIds = quotes.map((quote)
=> ({
value: quote,
id: crypto.randomUUID(),
}));
return (
<List
items={quotesWithIds}
selectedItemId={selectedQuoteId}
onSelectItem={setSelectedQuoteId}
/>
);
}
✅ Good: The IDs are added when we get the quotes.
function App() {
const [quotes, setQuotes] = useState([]);
const [selectedQuoteId,
setSelectedQuoteId] = useState(undefined);
// Fetch quotes and save with ID
useEffect(() => {
const loadQuotes = () =>
fetchQuotes().then((result) => {
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 42 / 140
// We add the `ids` as soon as we
get the results
setQuotes(
result.map((quote) => ({
value: quote,
id: crypto.randomUUID(),
}))
);
});
loadQuotes();
}, []);
return (
<List
items={quotes}
selectedItemId={selectedQuoteId}
onSelectItem={setSelectedQuoteId}
/>
);
}
➥ Tip # 22. Strategically use the key attribute
to trigger component re-renders
Want to force a component to re-render from scratch?
Just change its key .
In the example below, we use this trick to reset the error
boundary when switching to a new tab.
🏖 Sandbox
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 43 / 140
➥ Tip # 23. Use a ref callback function
for tasks such as monitoring size changes and
managing multiple node elements.
Did you know you can pass a function to the ref
attribute instead of a ref object?
Here's how it works:
When the DOM node is added to the screen, React
calls the function with the DOM node as the
argument.
When the DOM node is removed, React calls the
function with null .
In the example below, we use this tip to skip the
useEffect
❌ Before: Using useEffect to focus the input
function App() {
const ref = useRef();
useEffect(() => {
ref.current?.focus();
}, []);
return <input ref={ref} type="text" />;
}
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 44 / 140
✅ After: We focus on the input as soon as it is
available.
function App() {
const ref = useCallback((inputNode) => {
inputNode?.focus();
}, []);
return <input ref={ref} type="text" />;
}
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 45 / 140
CATEGORY #4: ORGANIZING REACT
CODE 🧩
➥ Tip # 24. Colocate React components with
their assets (e.g., styles, images, etc.)
Always keep each React component with related assets,
like styles and images.
This makes it easier to remove them when the
component is no longer needed.
It also simplifies code navigation, as everything
you need is in one place.
➥ Tip # 25. Limit your component file size
Big files with tons of components and exports can be
confusing.
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 46 / 140
Plus, they tend to grow even bigger as more stuff gets
added.
So, aim for a reasonable file size and split components
into separate files when it makes sense.
➥ Tip # 26. Limit the number of return
statements in your functional component file
Multiple return statements in a functional
component make it hard to see what the component is
returning.
This was not a problem for class components where we
could search for the render term.
A handy trick is to use arrow functions without braces
when possible (VSCode has an action for this ). 😀
❌ Bad: It is harder to spot the component return
statement
function Dashboard({ posts, searchTerm,
onPostSelect }) {
const filteredPosts = posts.filter((post)
=> {
return post.title.includes(searchTerm);
});
const createPostSelectHandler = (post) =>
{
return () => {
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 47 / 140
onPostSelect(post.id);
};
};
return (
<>
<h1>Posts</h1>
<ul>
{filteredPosts.map((post) => {
return (
<li key={post.id} onClick=
{createPostSelectHandler(post)}>
{post.title}
</li>
);
})}
</ul>
</>
);
}
✅ Good: There is one return statement for the
component
function Dashboard({ posts, searchTerm,
onPostSelect, selectedPostId }) {
const filteredPosts = posts.filter((post)
=> post.title.includes(searchTerm));
const createPostSelectHandler = (post) =>
() => {
onPostSelect(post.id);
};
return (
<>
<h1>Posts</h1>
<ul>
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 48 / 140
{filteredPosts.map((post) => (
<li
key={post.id}
onClick=
{createPostSelectHandler(post)}
style={{ color: post.id ===
selectedPostId ? "red" : "black" }}
>
{post.title}
</li>
))}
</ul>
</>
);
}
➥ Tip # 27. Prefer named exports over default
exports
I see default exports everywhere, and it makes me sad
🥲 .
Let's compare the two approaches:
/// `Dashboard` is exported as the default
component
export function Dashboard(props) {
/// TODO
}
/// `Dashboard` export is named
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 49 / 140
export function Dashboard(props) {
/// TODO
}
We now import the component like this:
/// Default export
import Dashboard from "/path/to/Dashboard";
/// Named export
import { Dashboard } from
"/path/to/Dashboard";
These are the problems with default exports:
If the component is renamed, the IDE won't
automatically rename the export.
For example, if Dashboard is renamed into Console ,
we will have this:
/// In the default export case, the name is
not changed
import Dashboard from "/path/to/Console";
/// In the named export case, the name is
changed
import { Console } from "/path/to/Console";
It's harder to see what is exported from a file with
default exports.
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 50 / 140
For example, in the case of named imports, once I type
import { } from "/path/to/file" , I get
autocompletion when I set my cursor inside the
brackets.
Default exports are harder to re-export.
For example, if I wanted to re-export the Dashboard
component from let's say an index file, I would have to
do this:
export { default as Dashboard } from
"/path/to/Dashboard";
The solution is more straightforward with named
exports.
export { Dashboard } from
"/path/to/Dashboard";
So, please default to named exports 🙏.
💡 Note: Even if you're using React lazy you can
still use named exports. See an example here.
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 51 / 140
CATEGORY #5: EFFICIENT STATE
MANAGEMENT 🚦
➥ Tip # 28. Never create a state for a value that
can be derived from other state or props
More state = more trouble.
Every piece of state can trigger a re-render and make
resetting the state a hassle.
So, if a value can be derived from state or props, skip
adding a new state.
❌ Bad: filteredPosts doesn't need to be in the
state.
function App({ posts }) {
const [filters, setFilters] = useState();
const [filteredPosts, setFilteredPosts] =
useState([]);
useEffect(() => {
setFilteredPosts(filterPosts(posts,
filters));
}, [posts, filters]);
return (
<Dashboard>
<Filters filters={filters}
onFiltersChange={setFilters} />
{filteredPosts.length > 0 && <Posts
posts={filteredPosts} />}
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 52 / 140
</Dashboard>
);
}
✅ Good: filteredPosts is derived from posts
and filters.
function App({ posts }) {
const [filters, setFilters] =
useState({});
const filteredPosts = filterPosts(posts,
filters);
return (
<Dashboard>
<Filters filters={filters}
onFiltersChange={setFilters} />
{filteredPosts.length > 0 && <Posts
posts={filteredPosts} />}
</Dashboard>
);
}
➥ Tip # 29. Keep the state at the lowest level
necessary to minimize re-renders
Whenever the state changes inside a component, React
re-renders the component and all its children (there is
an exception with children wrapped in memo).
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 53 / 140
This happens even if those children don't use the
changed state. To minimize re-renders, move the state
down the component tree as far as possible.
❌ Bad: When sortOrder changes, both
LeftSidebar and RightSidebar re-render.
function App() {
const [sortOrder, setSortOrder] =
useState("popular");
return (
<div className="App">
<LeftSidebar />
<Main sortOrder={sortOrder}
setSortOrder={setSortOrder} />
<RightSidebar />
</div>
);
}
function Main({ sortOrder, setSortOrder })
{
return (
<div>
<Button
onClick={() =>
setSortOrder("popular")}
active={sortOrder === "popular"}
>
Popular
</Button>
<Button
onClick={() =>
setSortOrder("latest")}
active={sortOrder === "latest"}
>
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 54 / 140
Latest
</Button>
</div>
);
}
✅ Good: sortOrder change will only affect Main .
function App() {
return (
<div className="App">
<LeftSidebar />
<Main />
<RightSidebar />
</div>
);
}
function Main() {
const [sortOrder, setSortOrder] =
useState("popular");
return (
<div>
<Button
onClick={() =>
setSortOrder("popular")}
active={sortOrder === "popular"}
>
Popular
</Button>
<Button
onClick={() =>
setSortOrder("latest")}
active={sortOrder === "latest"}
>
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 55 / 140
Latest
</Button>
</div>
);
}
➥ Tip # 30. Clarify the distinction between the
initial state and the current state
❌ Bad: It's unclear that sortOrder is just the initial
value, which may lead to confusion or errors in state
management.
function Main({ sortOrder }) {
const [internalSortOrder,
setInternalSortOrder] =
useState(sortOrder);
return (
<div>
<Button
onClick={() =>
setInternalSortOrder("popular")}
active={internalSortOrder ===
"popular"}
>
Popular
</Button>
<Button
onClick={() =>
setInternalSortOrder("latest")}
active={internalSortOrder ===
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 56 / 140
"latest"}
>
Latest
</Button>
</div>
);
}
✅ Good: Naming is clear about what is the initial state
and what is current.
function Main({ initialSortOrder }) {
const [sortOrder, setSortOrder] =
useState(initialSortOrder);
return (
<div>
<Button
onClick={() =>
setSortOrder("popular")}
active={sortOrder === "popular"}
>
Popular
</Button>
<Button
onClick={() =>
setSortOrder("latest")}
active={sortOrder === "latest"}
>
Latest
</Button>
</div>
);
}
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 57 / 140
➥ Tip # 31. Update state based on the previous
state, especially when memoizing with
useCallback
React allows you to pass an updater function to the
set function from useState .
This updater function uses the current state to calculate
the next state.
I use this behavior whenever I need to update the state
based on the previous state, especially inside functions
wrapped with useCallback. In fact, this approach
prevents the need to have the state as one of the hook
dependencies.
❌ Bad: handleAddTodo and handleRemoveTodo
change whenever todos changes.
function App() {
const [todos, setToDos] = useState([]);
const handleAddTodo = useCallback(
(todo) => {
setToDos([...todos, todo]);
},
[todos]
);
const handleRemoveTodo = useCallback(
(id) => {
setToDos(todos.filter((todo) =>
todo.id !== id));
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 58 / 140
},
[todos]
);
return (
<div className="App">
<TodoInput onAddTodo={handleAddTodo}
/>
<TodoList todos={todos} onRemoveTodo=
{handleRemoveTodo} />
</div>
);
}
✅ Good: handleAddTodo and handleRemoveTodo
remain the same even when todos changes.
function App() {
const [todos, setToDos] = useState([]);
const handleAddTodo = useCallback((todo)
=> {
setToDos((prevTodos) => [...prevTodos,
todo]);
}, []);
const handleRemoveTodo = useCallback((id)
=> {
setToDos((prevTodos) =>
prevTodos.filter((todo) => todo.id !==
id));
}, []);
return (
<div className="App">
<TodoInput onAddTodo={handleAddTodo}
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 59 / 140
/>
<TodoList todos={todos} onRemoveTodo=
{handleRemoveTodo} />
</div>
);
}
➥ Tip # 32. Use functions in useState for lazy
initialization and performance gains, as they are
invoked only once.
Using a function in useState ensures the initial state is
computed only once.
This can improve performance, especially when the
initial state is derived from an "expensive" operation like
reading from local storage.
❌ Bad: We read the theme from local storage every
time the component renders
const THEME_LOCAL_STORAGE_KEY = "101-react-
tips-theme";
function PageWrapper({ children }) {
const [theme, setTheme] = useState(
localStorage.getItem(THEME_LOCAL_STORAGE_KE
Y) || "dark"
);
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 60 / 140
const handleThemeChange = (theme) => {
setTheme(theme);
localStorage.setItem(THEME_LOCAL_STORAGE_KE
Y, theme);
};
return (
<div
className="page-wrapper"
style={{ background: theme === "dark"
? "black" : "white" }}
>
<div className="header">
<button onClick={() =>
handleThemeChange("dark")}>Dark</button>
<button onClick={() =>
handleThemeChange("light")}>Light</button>
</div>
<div>{children}</div>
</div>
);
}
✅ Good: We only read from the local storage when the
component mounts.
function PageWrapper({ children }) {
const [theme, setTheme] = useState(
() =>
localStorage.getItem(THEME_LOCAL_STORAGE_KE
Y) || "dark"
);
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 61 / 140
const handleThemeChange = (theme) => {
setTheme(theme);
localStorage.setItem(THEME_LOCAL_STORAGE_KE
Y, theme);
};
return (
<div
className="page-wrapper"
style={{ background: theme === "dark"
? "black" : "white" }}
>
<div className="header">
<button onClick={() =>
handleThemeChange("dark")}>Dark</button>
<button onClick={() =>
handleThemeChange("light")}>Light</button>
</div>
<div>{children}</div>
</div>
);
}
➥ Tip # 33. Use react context for broadly
needed, static state to prevent prop drilling
I will use React context whenever I have some data that:
Is needed in multiple places (e.g., theme, current
user, etc.)
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 62 / 140
Is mostly static or read-only (i.e., the user
can't/doesn't change the data often)
This approach helps avoid prop drilling (i.e., passing
down data or state through multiple layers of the
component hierarchy).
See an example in the sandbox below 👇.
🏖 https://codesandbox.io/p/sandbox/sad-farrell-
t4cqqp
➥ Tip # 34. React Context: Split your context
into parts that change frequently and those that
change infrequently to enhance app
performance
One challenge with React context is that all components
consuming the context re-render whenever the context
data changes, even if they don't use the part of the
context that changed .
A solution? Use separate contexts.
In the example below, we've created two contexts: one for
actions (which are constant) and another for state
(which can change).
🏖 https://codesandbox.io/p/sandbox/context-
split-vdny3w
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 63 / 140
➥ Tip # 35. React Context: Introduce a
Provider component when the value
computation is not straightforward
❌ Bad: There is too much logic inside App to manage
the theme.
const THEME_LOCAL_STORAGE_KEY = "101-react-
tips-theme";
const DEFAULT_THEME = "light";
const ThemeContext = createContext({
theme: DEFAULT_THEME,
setTheme: () => null,
});
function App() {
const [theme, setTheme] = useState(
() =>
localStorage.getItem(THEME_LOCAL_STORAGE_KE
Y) || DEFAULT_THEME
);
useEffect(() => {
if (theme !== "system") {
updateRootElementTheme(theme);
return;
}
// We need to get the class to apply
based on the system theme
const systemTheme = window.matchMedia("
(prefers-color-scheme: dark)")
.matches
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 64 / 140
? "dark"
: "light";
updateRootElementTheme(systemTheme);
// Then watch for changes in the system
theme and update the root element
accordingly
const darkThemeMq = window.matchMedia("
(prefers-color-scheme: dark)");
const listener = (event) => {
updateRootElementTheme(event.matches
? "dark" : "light");
};
darkThemeMq.addEventListener("change",
listener);
return () =>
darkThemeMq.removeEventListener("change",
listener);
}, [theme]);
const themeContextValue = {
theme,
setTheme: (theme) => {
localStorage.setItem(THEME_LOCAL_STORAGE_KE
Y, theme);
setTheme(theme);
},
};
const [selectedPostId, setSelectedPostId]
= useState(undefined);
const onPostSelect = (postId) => {
// TODO: some logging
setSelectedPostId(postId);
};
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 65 / 140
const posts = useSWR("/api/posts",
fetcher);
return (
<div className="App">
<ThemeContext.Provider value=
{themeContextValue}>
<Dashboard
posts={posts}
onPostSelect={onPostSelect}
selectedPostId={selectedPostId}
/>
</ThemeContext.Provider>
</div>
);
}
✅ Good: The theme logic is encapsulated in
ThemeProvider
function App() {
const [selectedPostId, setSelectedPostId]
= useState(undefined);
const onPostSelect = (postId) => {
// TODO: some logging
setSelectedPostId(postId);
};
const posts = useSWR("/api/posts",
fetcher);
return (
<div className="App">
<ThemeProvider>
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 66 / 140
<Dashboard
posts={posts}
onPostSelect={onPostSelect}
selectedPostId={selectedPostId}
/>
</ThemeProvider>
</div>
);
}
function ThemeProvider({ children }) {
const [theme, setTheme] = useState(
() =>
localStorage.getItem(THEME_LOCAL_STORAGE_KE
Y) || DEFAULT_THEME
);
useEffect(() => {
if (theme !== "system") {
updateRootElementTheme(theme);
return;
}
// We need to get the class to apply
based on the system theme
const systemTheme = window.matchMedia("
(prefers-color-scheme: dark)")
.matches
? "dark"
: "light";
updateRootElementTheme(systemTheme);
// Then watch for changes in the system
theme and update the root element
accordingly
const darkThemeMq = window.matchMedia("
(prefers-color-scheme: dark)");
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 67 / 140
const listener = (event) => {
updateRootElementTheme(event.matches
? "dark" : "light");
};
darkThemeMq.addEventListener("change",
listener);
return () =>
darkThemeMq.removeEventListener("change",
listener);
}, [theme]);
const themeContextValue = {
theme,
setTheme: (theme) => {
localStorage.setItem(THEME_LOCAL_STORAGE_KE
Y, theme);
setTheme(theme);
},
};
return (
<div className="App">
<ThemeContext.Provider value=
{themeContextValue}>
{children}
</ThemeContext.Provider>
</div>
);
}
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 68 / 140
➥ Tip # 36. Consider using the useReducer
hook as a lightweight state management solution
Whenever I have too many values in my state or a
complex state and don't want to rely on external
libraries, I will reach for useReducer .
It's especially effective when combined with context for
broader state management needs.
Example: See #Tip 34.
➥ Tip # 37. Simplify state updates with
useImmer or useImmerReducer
With hooks like useState and useReducer , the
state must be immutable (i.e., all changes need to create
a new state vs. modifying the current one).
This is often cumbersome to achieve.
This is where useImmer and useImmerReducer offer a
simpler alternative. They allow you to write "mutable"
code automatically converted to immutable updates.
❌ Tedious: We must carefully ensure we're creating a
new state object.
export function App() {
const [{ email, password }, setState] =
useState({
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 69 / 140
email: "",
password: "",
});
const onEmailChange = (event) => {
setState((prevState) => ({
...prevState, email: event.target.value
}));
};
const onPasswordChange = (event) => {
setState((prevState) => ({
...prevState, password: event.target.value
}));
};
return (
<div className="App">
<h1>Welcome</h1>
<p>
Email: <input type="email" value=
{email} onChange={onEmailChange} />
</p>
<p>
Password:{" "}
<input type="password" value=
{password} onChange={onPasswordChange} />
</p>
</div>
);
}
✅ More straightforward: We can directly modify
draftState .
import { useImmer } from "use-immer";
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 70 / 140
export function App() {
const [{ email, password }, setState] =
useImmer({
email: "",
password: "",
});
const onEmailChange = (event) => {
setState((draftState) => {
draftState.email =
event.target.value;
});
};
const onPasswordChange = (event) => {
setState((draftState) => {
draftState.password =
event.target.value;
});
};
/// Rest of logic
}
➥ Tip # 38. Use Redux (or another state
management solution) for complex client-side
state accessed across multiple components
I turn to Redux whenever:
I have a complex FE app with a lot of shared client-
side state (for example, dashboard apps)
I want the user to be able to go back in time and
revert changes
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 71 / 140
I don't want my components to re-render
unnecessarily like they can with React context
I have too many contexts that start getting out of
control
For a streamlined experience, I recommend using redux-
tooltkit.
💡 Note: You can also consider other alternatives
to Redux, such as Zustand or Recoil.
➥ Tip # 39. Redux: Use Redux DevTools to
debug your state
The Redux DevTools browser extension is an useful tool
for debugging your Redux projects.
It allows you to visualize your state and actions in real-
time, maintain state persistence across refreshes, and
much more.
To understand its use, watch this great YouTube video.
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 72 / 140
CATEGORY #6: REACT CODE
OPTIMIZATION 🚀
➥ Tip # 40. Prevent unnecessary re-renders
with memo
When dealing with components that are costly to render
and their parent components frequently update,
wrapping them in memo can be a game changer.
memo ensures that a component only re-renders when
its props have changed, not simply because its parent
re-rendered.
In the example below, I get some data from the server
through useGetDashboardData . If the posts didn't
change, wrapping ExpensiveList in memo will
prevent it from re-rendering when other parts of the data
update.
export function App() {
const { profileInfo, posts } =
useGetDashboardData();
return (
<div className="App">
<h1>Dashboard</h1>
<Profile data={profileInfo} />
<ExpensiveList posts={posts} />
</div>
);
}
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 73 / 140
const ExpensiveList = memo(({ posts }) => {
/// Rest of implementation
});
💡 : This tip may be irrelevant once React compiler
gets stable 😅 .
➥ Tip # 41. Specify an equality function with
memo to instruct React on how to compare the
props.
By default, memo uses Object.is to compare each prop
with its previous value.
However, specifying a custom equality function can be
more efficient than default comparisons or re-rendering
for more complex or specific scenarios.
Example 👇
const ExpensiveList = memo(
({ posts }) => {
return <div>{JSON.stringify(posts)}
</div>;
},
(prevProps, nextProps) => {
// Only re-render if the last post or
the list size changes
const prevLastPost =
prevProps.posts[prevProps.posts.length -
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 74 / 140
1];
const nextLastPost =
nextProps.posts[nextProps.posts.length -
1];
return (
prevLastPost.id === nextLastPost.id
&&
prevProps.posts.length ===
nextProps.posts.length
);
}
);
➥ Tip # 42. Prefer named functions over arrow
functions when declaring a memoized
component
When defining memoized components, using named
functions instead of arrow functions can improve clarity
in React DevTools.
Arrow functions often result in generic names like _c2 ,
making debugging and profiling more difficult.
❌ Bad: Using arrow functions for memoized
components results in less informative names in React
DevTools.
const ExpensiveList = memo(({ posts }) => {
/// Rest of implementation
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 75 / 140
});
ExpensiveList name is not visible
✅ Good: The component's name will be visible in
DevTools.
const ExpensiveList = memo(function
ExpensiveListFn({ posts }) {
/// Rest of implementation
});
You can see ExpensiveListFn in DevTools
➥ Tip # 43. Cache expensive computations or
preserve references with useMemo
I will generally useMemo :
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 76 / 140
When I have expensive computations that should
not be repeated on each render.
If the computed value is a non-primitive value
that is used as a dependency in hooks like
useEffect .
The computed non-primitive value will be passed
as a prop to a component wrapped in memo ;
otherwise, this will break the memoization since
React uses Object.is to detect whether props
changed.
❌ Bad: The memo for ExpensiveList does not
prevent re-renders because styles are recreated on every
render.
export function App() {
const { profileInfo, posts, baseStyles }
= useGetDashboardData();
// We get a new `styles` object on every
render
const styles = { ...baseStyles, padding:
"10px" };
return (
<div className="App">
<h1>Dashboard</h1>
<Profile data={profileInfo} />
<ExpensiveList posts={posts} styles=
{styles} />
</div>
);
}
const ExpensiveList = memo(function
ExpensiveListFn({ posts, styles }) {
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 77 / 140
/// Rest of implementation
});
✅ Good: The use of useMemo ensures styles only
changes when baseStyles changes, allowing memo
to effectively prevent unnecessary re-renders.
export function App() {
const { profileInfo, posts, baseStyles }
= useGetDashboardData();
// We get a new `styles` object only if
`baseStyles` changes
const styles = useMemo(
() => ({ ...baseStyles, padding: "10px"
}),
[baseStyles]
);
return (
<div className="App">
<h1>Dashboard</h1>
<Profile data={profileInfo} />
<ExpensiveList posts={posts} styles=
{styles} />
</div>
);
}
➥ Tip # 44. Use useCallback to memoize
functions
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 78 / 140
useCallback is similar to useMemo but is designed
explicitly for memoizing functions.
❌ Bad: Whenever the theme changes,
handleThemeChange will be called twice, and we will
push logs to the server twice.
function useTheme() {
const [theme, setTheme] =
useState("light");
// `handleThemeChange` changes on every
render
// As a result, the effect will be
triggered after each render
const handleThemeChange = (newTheme) => {
pushLog(["Theme changed"], {
context: {
theme: newTheme,
},
});
setTheme(newTheme);
};
useEffect(() => {
const dqMediaQuery =
window.matchMedia("(prefers-color-scheme:
dark)");
handleThemeChange(dqMediaQuery.matches
? "dark" : "light");
const listener = (event) => {
handleThemeChange(event.matches ?
"dark" : "light");
};
dqMediaQuery.addEventListener("change",
listener);
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 79 / 140
return () => {
dqMediaQuery.removeEventListener("change",
listener);
};
}, [handleThemeChange]);
return theme;
}
✅ Good: Wrapping handleThemeChange in
useCallback ensures that it is recreated only when
necessary, reducing unnecessary executions.
const handleThemeChange =
useCallback((newTheme) => {
pushLog(["Theme changed"], {
context: {
theme: newTheme,
},
});
setTheme(newTheme);
}, []);
➥ Tip # 45. Memoize callbacks or values
returned from utility hooks to avoid performance
issues
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 80 / 140
When you create a custom hook to share with others,
memoizing the returned values and functions is crucial.
This practice makes your hook more efficient and
prevents unnecessary performance issues for anyone
using it.
❌ Bad: loadData is not memoized and creates
performance issues.
function useLoadData(fetchData) {
const [result, setResult] = useState({
type: "notStarted",
});
async function loadData() {
setResult({ type: "loading" });
try {
const data = await fetchData();
setResult({ type: "loaded", data });
} catch (err) {
setResult({ type: "error", error: err
});
}
}
return { result, loadData };
}
✅ Good: We memoize everything so there are no
unexpected performance issues.
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 81 / 140
function useLoadData(fetchData) {
const [result, setResult] = useState({
type: "notStarted",
});
// Wrap in `useRef` and use the `ref`
value so the function never changes
const fetchDataRef = useRef(fetchData);
useEffect(() => {
fetchDataRef.current = fetchData;
}, [fetchData]);
// Wrap in `useCallback` and use the
`ref` value so the function never changes
const loadData = useCallback(async () =>
{
setResult({ type: "loading" });
try {
const data = await
fetchDataRef.current();
setResult({ type: "loaded", data });
} catch (err) {
setResult({ type: "error", error: err
});
}
}, []);
return useMemo(() => ({ result, loadData
}), [result, loadData]);
}
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 82 / 140
➥ Tip # 46. Leverage lazy loading and
Suspense to make your apps load faster
When you're building your app, consider using lazy
loading and Suspense for code that is:
Expensive to load.
Only relevant to some users (like premium
features).
Not immediately necessary for the initial user
interaction.
In the sandbox below 👇
, the Slider assets (JS + CSS)
only load after you click on a card.
🏖 https://stackblitz.com/edit/vitejs-vite-czefdj?
file=src%2FApp.jsx
Slider assets only load when needed
➥ Tip # 47. Throttle your network to simulate a
slow network
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 83 / 140
Did you know you can simulate slow internet
connections directly in Chrome?
This is especially useful when:
Customers report slow loading times that you can't
replicate on your faster network.
You're implementing lazy loading and want to
observe how files load under slower conditions to
ensure appropriate loading states.
💡 Find out how to do it on this link.
➥ Tip # 48. Use react-window or react-
virtuoso to e ciently render lists
Never render a long list of items all at once—such as
chat messages, logs, or infinite lists.
Doing so can cause the browser to freeze.
Instead, virtualize the list. This means rendering only the
subset of items likely to be visible to the user.
Libraries like react-window, react-virtuoso or
@tanstack/react-virtual are designed for this purpose.
❌ Bad: NonVirtualList renders all 50,000 log lines
simultaneously, even if they aren't visible.
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 84 / 140
function NonVirtualList({ items }) {
return (
<div style={{ height: "100%" }}>
{items.map((log, index) => (
<div
key={log.id}
style={{
padding: "5px",
borderBottom:
index === items.length - 1 ?
"none" : "1px solid #ccc",
}}
>
<LogLine log={log} index={index}
/>
</div>
))}
</div>
);
}
✅ Good: VirtualList renders only the items likely
to be visible.
function VirtualList({ items }) {
return (
<Virtuoso
style={{ height: "100%" }}
data={items}
itemContent={(index, log) => (
<div
key={log.id}
style={{
padding: "5px",
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 85 / 140
borderBottom:
index === items.length - 1 ?
"none" : "1px solid #ccc",
}}
>
<LogLine log={log} index={index}
/>
</div>
)}
/>
);
}
You can switch between the two options in the sandbox
below and notice how bad the app performs when
NonVirtualList is used . 👇
🏖 https://codesandbox.io/p/sandbox/virtual-list-
9qfh5y
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 86 / 140
CATEGORY #7: DEBUGGING REACT
CODE 🐞
➥ Tip # 49. Use StrictMode to catch bugs in
your components before deploying them to
production
Using StrictMode is a proactive way to detect potential
issues in your application during development.
It helps identify problems such as:
Incomplete cleanup in effects, like forgetting to
release resources.
Impurities in React components, ensuring they
return consistent JSX given the same inputs
(props, state, and context).
The example below shows a bug because
clearInterval is never called. StrictMode helps
catch this by running the effect twice, which creates two
intervals.
🏖 https://codesandbox.io/p/sandbox/strict-
mode-example-pyrcg8
➥ Tip # 50. Install the React Developer Tools
browser extension to view/edit your components
and detect performance issues
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 87 / 140
React Developer Tools is a must extension (Chrome,
Firefox).
This extension lets you:
Visualize and delve into the details of your React
components, examining everything from props to
state.
Directly modify a component's state or props to see
how changes affect behavior and rendering.
Profile your application to identify when and why
components are re-rendering, helping you spot
performance issues.
Etc.
💡 Learn how to use it in this great guide.
➥ Tip # 51. React DevTools Components:
Highlight components that render to identify
potential issues
I will use this trick whenever I suspect that my app has
performance issues. You can highlight the components
that render to detect potential problems (e.g., too many
renders).
The gif below shows that the FollowersListFn
component re-renders whenever the time changes,
which is wrong.
💡 You can see it in action on this link.
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 88 / 140
➥ Tip # 52. Leverage useDebugValue in your
custom hooks for better visibility in React
DevTools
useDebugValue can be a handy tool for adding
descriptive labels to your custom hooks in React
DevTools.
This makes it easier to monitor their states directly from
the DevTools interface.
For instance, consider this custom hook I use to fetch
and display the current time, updated every second:
function useCurrentTime() {
const [time, setTime] = useState(new
Date());
useEffect(() => {
const intervalId = setInterval(() => {
setTime(new Date());
}, 1_000);
return () => clearInterval(intervalId);
}, [setTime]);
return time;
}
❌ Bad: Without useDebugValue , the actual time
value isn't immediately visible; you'd need to expand the
CurrentTime hook:
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 89 / 140
Current time not quickly visible on the
devtools
✅ Good: With useDebugValue , the current time is
readily visible:
useDebugValue(time);
Current time quickly visible on the devtools
Note: Use useDebugValue judiciously. It's best
reserved for complex hooks in shared libraries
where understanding the internal state is crucial.
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 90 / 140
➥ Tip # 53. Use the why-did-you-render
library to track component rendering and identify
potential performance bottlenecks
Sometimes, a component re-renders, and it's not
immediately clear why .
While React DevTools is helpful, in large apps, it might
only provide vague explanations like "hook #1 rendered,"
which can be useless.
App rendering because of hook 1 change
In such cases, you can turn to the why-did-you-render
library. It offers more detailed insights into why
components re-render, helping to pinpoint performance
issues more effectively.
I made an example in the sandbox below 👇 . Thanks to
this library, we can find the issue with the
FollowersList component.
🏖 https://codesandbox.io/p/sandbox/why-did-
you-render-sandbox-forked-nc4fnk
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 91 / 140
why-did-you-render console logs
➥ Tip # 54. Hide logs during the second render
in Strict Mode
StrictMode helps catch bugs early in your application's
development.
However, since it causes components to render twice,
this can result in duplicated logs, which might clutter
your console.
You can hide logs during the second render in Strict
Mode to address this.
Check out how to do it in the gif below 👇:
💡 Hide logs during the second render in Strict
Mode
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 92 / 140
CATEGORY #8: TESTING REACT
CODE 🧪
➥ Tip # 55. Use React Testing Library to
test your React components e ectively
Want to test your React apps?
Make sure to use @testing-library/react.
You can find a minimal example here.
➥ Tip # 56. React Testing Library: Use testing
playground to e ortlessly create queries
Struggling to decide which queries to use in your tests?
Consider using testing playground to quickly generate
them from your component's HTML.
Here are two ways to leverage it:
Option #1: Use
screen.logTestingPlaygroundURL() in your test.
This function generates a URL that opens the Testing
Playground tool with the HTML of your component
already loaded.
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 93 / 140
Option #2: Install Testing Playground Chrome extension.
This extension allows you to hover over elements in your
app directly in the browser to find the best queries for
testing them.
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 94 / 140
➥ Tip # 57. Conduct end-to-end tests with
Cypress or Playwright
Need to conduct end-to-end tests?
Make sure to check out Cypress or Playwright.
Note: Playwright's support for components is
experimental at the time of writing.
➥ Tip # 58. Use MSW to mock network requests
in your tests
Sometimes, your tests need to make network requests.
Rather than implementing your own mocks (or, God
forbid, making actual network requests ), consider 😅
using MSW (Mock Service Worker) to handle your API
responses.
MSW allows you to intercept and manipulate network
interactions directly in your tests, providing a robust
and straightforward solution for simulating server
responses without affecting live servers.
This approach helps maintain a controlled and
predictable testing environment, enhancing the
reliability of your tests.
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 95 / 140
CATEGORY #9: REACT HOOKS 🎣
➥ Tip # 59. Make sure you perform any required
cleanup in your useEffect hooks
Always return a cleanup function in your useEffect
hooks if you're setting up anything that needs to be
cleaned up later.
This could be anything from ending a chat session to
closing a database connection.
Neglecting this step can lead to poor resource usage and
potential memory leaks.
❌ Bad: This example sets an interval. But we never
clear it, which means it keeps running even after the
component is unmounted.
function Timer() {
const [time, setTime] = useState(new
Date());
useEffect(() => {
setInterval(() => {
setTime(new Date());
}, 1_000);
}, []);
return <>Current time
{time.toLocaleTimeString()}</>;
}
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 96 / 140
✅ Good: The interval is properly cleared when the
component unmounts.
function Timer() {
const [time, setTime] = useState(new
Date());
useEffect(() => {
const intervalId = setInterval(() => {
setTime(new Date());
}, 1_000);
// We clear the interval
return () => clearInterval(intervalId);
}, []);
return <>Current time
{time.toLocaleTimeString()}</>;
}
➥ Tip # 60. Use refs for accessing DOM
elements
You should never manipulate the DOM directly with
React.
Methods like document.getElementById and
document.getElementsByClassName are forbidden
since React should access/manipulate the DOM.
So, what should you do when you need access to DOM
elements?
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 97 / 140
You can use the useRef hook, like in the example below,
where we need access to the canvas element.
🏖 https://codesandbox.io/p/sandbox/chart-js-
example-xdrnqv
Note: We could have added an ID to the canvas and
used document.getElementById , but this is
not recommended.
➥ Tip # 61. Use refs to preserve values
across re-renders
If you have mutable values in your React component
that aren't stored in the state, you'll notice that changes
to these values don't persist through re-renders.
This happens unless you save them globally.
You might consider putting these values in the state.
However, if they are irrelevant to the rendering, doing so
can cause unnecessary re-renders, which wastes
performance.
This is where useRef also shines.
In the example below, I want to stop the timer when the
user clicks on some button. For that, I need to store the
intervalId somewhere.
❌ Bad: The example below won't work as intended
because intervalId gets reset with every component
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 98 / 140
re-render.
function Timer() {
const [time, setTime] = useState(new
Date());
let intervalId;
useEffect(() => {
intervalId = setInterval(() => {
setTime(new Date());
}, 1_000);
return () => clearInterval(intervalId);
}, []);
const stopTimer = () => {
intervalId &&
clearInterval(intervalId);
};
return (
<>
<>Current time:
{time.toLocaleTimeString()} </>
<button onClick={stopTimer}>Stop
timer</button>
</>
);
}
✅ Good: By using useRef , we ensure that the interval
ID is preserved between renders.
function Timer() {
const [time, setTime] = useState(new
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 99 / 140
Date());
const intervalIdRef = useRef();
const intervalId = intervalIdRef.current;
useEffect(() => {
const interval = setInterval(() => {
setTime(new Date());
}, 1_000);
intervalIdRef.current = interval;
return () => clearInterval(interval);
}, []);
const stopTimer = () => {
intervalId &&
clearInterval(intervalId);
};
return (
<>
<>Current time:
{time.toLocaleTimeString()} </>
<button onClick={stopTimer}>Stop
timer</button>
</>
);
}
➥ Tip # 62. Prefer named functions over arrow
functions within hooks such as useEffect to
easily find them in React Dev Tools
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 100 / 140
If you have many hooks, finding them in React DevTools
can be challenging.
One trick is to use named functions so you can quickly
spot them.
❌ Bad: It's hard to find the specific effect among many
hooks.
function HelloWorld() {
useEffect(() => {
console.log(" 🚀
~ Hello, I just got
mounted");
}, []);
return <>Hello World</>;
}
Effect has no associated name
✅ Good: You can quickly spot the effect.
function HelloWorld() {
useEffect(function logOnMount() {
console.log(" 🚀
~ Hello, I just got
mounted");
}, []);
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 101 / 140
return <>Hello World</>;
}
Effect with associated name
➥ Tip # 63. Encapsulate logic with custom hooks
Let's say I have a component that gets the theme from
the user's dark mode preferences and uses it inside the
app.
It's better to extract the logic that returns the theme into
a custom hook (to reuse it and keep the component
clean).
❌ Bad: App is overcrowded
function App() {
const [theme, setTheme] =
useState("light");
useEffect(() => {
const dqMediaQuery =
window.matchMedia("(prefers-color-scheme:
dark)");
setTheme(dqMediaQuery.matches ? "dark"
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 102 / 140
: "light");
const listener = (event) => {
setTheme(event.matches ? "dark" :
"light");
};
dqMediaQuery.addEventListener("change",
listener);
return () => {
dqMediaQuery.removeEventListener("change",
listener);
};
}, []);
return (
<div className={`App ${theme === "dark"
? "dark" : ""}`}>Hello Word</div>
);
}
✅ Good: App is much simpler, and we can reuse the
logic
function App() {
const theme = useTheme();
return (
<div className={`App ${theme === "dark"
? "dark" : ""}`}>Hello Word</div>
);
}
// Custom hook that can be reused
function useTheme() {
const [theme, setTheme] =
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 103 / 140
useState("light");
useEffect(() => {
const dqMediaQuery =
window.matchMedia("(prefers-color-scheme:
dark)");
setTheme(dqMediaQuery.matches ? "dark"
: "light");
const listener = (event) => {
setTheme(event.matches ? "dark" :
"light");
};
dqMediaQuery.addEventListener("change",
listener);
return () => {
dqMediaQuery.removeEventListener("change",
listener);
};
}, []);
return theme;
}
➥ Tip # 64. Prefer functions over custom hooks
Never put logic inside a hook when a function can be
used 🛑
.
In effect:
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 104 / 140
Hooks can only be used inside other hooks or
components, whereas functions can be used
everywhere.
Functions are simpler than hooks.
Functions are easier to test.
Etc.
❌ Bad: The useLocale hook is unnecessary since it
doesn't need to be a hook. It doesn't use other hooks like
useEffect , useState , etc.
function App() {
const locale = useLocale();
return (
<div className="App">
<IntlProvider locale={locale}>
<BlogPost post={EXAMPLE_POST} />
</IntlProvider>
</div>
);
}
function useLocale() {
return window.navigator.languages?.[0] ??
window.navigator.language;
}
✅ Good: Create a function getLocale instead
function App() {
const locale = getLocale();
return (
<div className="App">
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 105 / 140
<IntlProvider locale={locale}>
<BlogPost post={EXAMPLE_POST} />
</IntlProvider>
</div>
);
}
function getLocale() {
return window.navigator.languages?.[0] ??
window.navigator.language;
}
➥ Tip # 65. Prevent visual UI glitches by using
the useLayoutEffect hook
When an effect isn't caused by a user interaction, the
user will see the UI before the effect runs (often briefly).
As a result, if the effect modifies the UI, the user will see
the initial UI version very quickly before seeing the
updated one, creating a visual glitch.
Using useLayoutEffect ensures the effect runs
synchronously after all DOM mutations, preventing the
initial render glitch.
In the sandbox below, we want the width to be equally
distributed between the columns (I know this can be
done in CSS, but I need an example ). 😅
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 106 / 140
With useEffect , you can see briefly at the beginning
that the table is changing. The columns are rendered
with their default size before being adjusted to their
correct size.
🏖 https://codesandbox.io/p/sandbox/use-
layout-effect-hqhnld
If you're looking for another great usage, check out this
post.
➥ Tip # 66. Generate unique IDs for
accessibility attributes with the useId hook
Tired of coming up with IDs or having them clash?
You can use the useId hook to generate a unique ID
inside your React component and ensure your app is
accessible.
Example
function Form() {
const id = useId();
return (
<div className="App">
<div>
<label>
Name <input type="text" aria-
describedby={id} />
</label>
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 107 / 140
</div>
<span id={id}>Make sure to include
full name</span>
</div>
);
}
➥ Tip # 67. Use the useSyncExternalStore
to subscribe to an external store
This is a rarely needed but super powerful hook 💪.
Use this hook if:
You have some state not accessible in the React
tree (i.e., not present in the state or context)
The state can change, and you need your
component to be notified of changes
In the example below, I want a Logger singleton to log
errors, warnings, info, etc., in my entire app.
These are the requirements:
I need to be able to call this everywhere in my React
app (even inside non-React components), so I
won't put it inside a state/context.
I want to display all the logs to the user inside a
Logs component
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 108 / 140
👉 I can use useSyncExternalStore inside my
Logs component to access the logs and listen to
changes.
function createLogger() {
let logs = [];
let listeners = [];
const pushLog = (log) => {
logs = [...logs, log];
listeners.forEach((listener) =>
listener());
};
return {
getLogs: () => Object.freeze(logs),
subscribe: (listener) => {
listeners.push(listener);
return () => {
listeners = listeners.filter((l) =>
l !== listener);
};
},
info: (message) => {
pushLog({ level: "info", message });
console.info(message);
},
error: (message) => {
pushLog({ level: "error", message });
console.error(message);
},
warn: (message) => {
pushLog({ level: "warn", message });
console.warn(message);
},
};
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 109 / 140
export const Logger = createLogger();
🏖 https://codesandbox.io/p/sandbox/sync-
external-storage-rkq2zq
➥ Tip # 68. Use the useDeferredValue hook
to display the previous query results until the
new results become available
Imagine you're building an app that represents countries
on a map.
Users can filter to see countries up to a specific
population size.
Every time maxPopulationSize updates, the map is
re-rendered (see sandbox below).
🏖 https://codesandbox.io/p/sandbox/countries-
app-use-deferred-value-4nh73p
As a result, notice how janky the slider is when you
move it too fast. This is because the map is re-rendered
every time the slider moves.
To solve this, we can use the useDeferredValue hook
so that the slider updates smoothly.
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 110 / 140
<Map
maxPopulationSize=
{deferredMaxPopulationSize}
// …
/>
If you're looking for another great usage, check out this
post.
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 111 / 140
CATEGORY #10: MUST-KNOWN
REACT LIBRARIES/TOOLS 🧰
➥ Tip # 69. Incorporate routing into your app
with react-router
If you need your app to support multiple pages, check
out react-router.
You can find a minimal example here.
➥ Tip # 70. Implement first-class data fetching in
your app with swr or React Query
Data fetching can be notoriously tricky.
However, libraries like swr or React Query make it much
easier.
I recommend swr for simple use cases and React Query
for more complex ones.
➥ Tip # 71. Simplify form state management with
libraries like formik , React Hook Form , or
TanStack Form
I used to hate form management in React 🥲.
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 112 / 140
That was until I discovered libraries like:
formik
React Hook Form, or
or TanStack Form
So make sure to check these out if you're struggling with
forms.
➥ Tip # 72. Internationalize your app using
Format.js, Lingui, or react-i18next.
If your app needs to support multiple languages, it
should be internationalized.
You can achieve this with libraries like:
Format.js
Lingui
react-i18next
➥ Tip # 73. E ortlessly create impressive
animations with framer-motion
Animations can make your app stand out 🔥.
You can create them easily with framer-motion.
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 113 / 140
➥ Tip # 74. Tired of re-inventing the wheel with
custom hooks? Check out https://usehooks.com/
If you're like me, you've written the same hooks over and
over again.
So check usehooks.com first to see if someone has
already done the work for you.
➥ Tip # 75. Streamline app development by
leveraging UI libraries like Shadcdn or Headless
UI
It's hard to build UI at scale that is accessible,
responsive, and beautiful.
Libraries like Shadcdn or Headless UI make it easier.
Shadcdn provides a set of accessible, reusable,
and composable React components that you can
copy and paste into your apps. At the time of
writing, it requires Tailwind CSS.
Headless UI provides unstyled, fully accessible UI
components that you can use to build your own UI
components.
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 114 / 140
➥ Tip # 76. Check your website's accessibility
with the axe-core-npm library
Websites should be accessible to everyone.
However, it's easy to miss accessibility issues.
axe-core-npm is a fast, secure, and reliable way to check
your website's accessibility while developing it.
💡 Tip: If you're a VSCode user, you can install the
associated extension: axe Accessibility Linter.
➥ Tip # 77. Refactor React code e ortlessly with
react-codemod
Codemods are transformations that run on your
codebase programmatically . 💻
They make it easy to refactor your codebase.
For example, React codemods can help you remove all
React imports from your codebase, update your code to
use the latest React features, and more.
So, make sure to check those out before manually
refactoring your code.
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 115 / 140
➥ Tip # 78. Transform your app into a
Progressive Web Application (PWA) using vite-
pwa
Progressive Web Applications (PWAs) load like regular
web pages but offer functionality such as working
offline, push notifications, and device hardware access.
You can easily create a PWA in React using vite-pwa.
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 116 / 140
CATEGORY #11: REACT & VISUAL
STUDIO CODE 🛠
➥ Tip # 79. Enhance your productivity with the
Simple React Snippets snippets extension
Bootstrapping a new React component can be tedious
😩 .
Snippets from the Simple React Snippets extension
make it easier.
➥ Tip # 80. Set
editor.stickyScroll.enabled to true
to quickly locate the current component
I love this feature ❤.
If you have a big file, it can be hard to locate the current
component.
By setting editor.stickyScroll.enabled to
true , the current component will always be at the top
of the screen.
❌ Without sticky scroll
💡 Without Sticky Scroll
✅ With sticky scroll
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 117 / 140
💡 With Sticky Scroll
➥ Tip # 81. Simplify refactoring with extensions
like VSCode Glean or VSCode React Refactor
If you need to refactor your code frequently (for example,
extract JSX into a new component), make sure to check
out extensions like VSCode Glean or VSCode React
Refactor.
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 118 / 140
CATEGORY #12: REACT &
TYPESCRIPT 🚀
➥ Tip # 82. Use ReactNode instead of
JSX.Element | null | undefined |
... to keep your code more compact
I see this mistake a lot.
Instead of typing the leftElement and
rightElement props like this:
const Panel = ({ leftElement, rightElement
}: {
leftElement:
| JSX.Element
| null
| undefined
// | ...;
rightElement:
| JSX.Element
| null
| undefined
// | ...
}) => {
// …
};
You can use ReactNode to keep the code more
compact.
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 119 / 140
const MyComponent = ({
leftElement,
rightElement,
}: {
leftElement: ReactNode,
rightElement: ReactNode,
}) => {
// …
};
➥ Tip # 83. Simplify the typing of components
expecting children props with
PropsWithChildren
You don't have to type the children prop manually.
In fact, you can use PropsWithChildren to simplify
the typings.
// 🟠 Ok
const HeaderPage = ({
children,
...pageProps
}: { children: ReactNode } & PageProps) =>
{
// …
};
// ✅Better
const HeaderPage = ({
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 120 / 140
children,
...pageProps
}: PropsWithChildren<PageProps>) => {
// …
};
➥ Tip # 84. Access element props e ciently
with ComponentProps ,
ComponentPropsWithoutRef ,…
There are cases where you need to figure out a
component's props.
For example, let's say you want a button that will log to
the console when clicked.
You can use ComponentProps to access the props of
the button element then override the click prop.
const ButtonWithLogging = (props:
ComponentProps<"button">) => {
const handleClick:
MouseEventHandler<HTMLButtonElement> = (e)
=> {
console.log("Button clicked"); //TODO:
Better logging
props.onClick?.(e);
};
return <button {...props} onClick=
{handleClick} />;
};
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 121 / 140
This trick also works with custom components.
const MyComponent = (props: { name: string
}) => {
// …
};
const MyComponentWithLogging = (props:
ComponentProps<typeof MyComponent>) => {
// …
};
➥ Tip # 85. Leverage types like
MouseEventHandler , FocusEventHandler
and others for concise typings
Rather than typing the event handlers manually, you can
use types like MouseEventHandler to keep the code
more concise and readable.
// 🟠 Ok
const MyComponent = ({
onClick,
onFocus,
onChange,
}: {
onClick: (e:
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 122 / 140
MouseEvent<HTMLButtonElement>) => void,
onFocus: (e:
FocusEvent<HTMLButtonElement>) => void,
onChange: (e:
ChangeEvent<HTMLInputElement>) => void,
}) => {
// …
};
// ✅ Better
const MyComponent = ({
onClick,
onFocus,
onChange,
}: {
onClick:
MouseEventHandler<HTMLButtonElement>,
onFocus:
FocusEventHandler<HTMLButtonElement>,
onChange:
ChangeEventHandler<HTMLInputElement>,
}) => {
// …
};
➥ Tip # 86. Specify types explicitly in useState,
useRef, etc., when the type can't be or shouldn't
be inferred from the initial value
Don't forget to specify the type when it can't be inferred
from the initial value.
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 123 / 140
For example, in the example below, there is a
selectedItemId stored in the state. It should be a
string or undefined .
since the type is not specified, TypeScript will infer the
type as undefined , which is not what we want.
// ❌Bad: `selectedItemId` will be
inferred as `undefined`
const [selectedItemId, setSelectedItemId] =
useState(undefined);
// ✅ Good
const [selectedItemId, setSelectedItemId] =
(useState < string) | (undefined >
undefined);
💡 Note: The opposite of this is that you don't need
to specify the type when TypeScript can infer it for
you.
➥ Tip # 87. Leverage the Record type for
cleaner and more extensible code
I love this helper type.
Let's say I have a type that represents log levels.
type LogLevel = "info" | "warn" | "error";
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 124 / 140
We have a corresponding function for each log level that
logs the message.
const logFunctions = {
info: (message: string) =>
console.info(message),
warn: (message: string) =>
console.warn(message),
error: (message: string) =>
console.error(message),
};
Instead of typing the logFunctions manually, you can
use the Record type.
const logFunctions: Record<LogLevel,
(message: string) => void> = {
info: (message) => console.info(message),
warn: (message) => console.warn(message),
error: (message) =>
console.error(message),
};
Using the Record type makes the code more concise
and readable.
Additionally, it helps capture any error if a new log level
is added or removed.
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 125 / 140
For example, if I decided to add a debug log level,
TypeScript would throw an error.
➥ Tip # 88. Use the as const trick to
accurately type your hook return values
Let's say we have a hook useIsHovered to detect
whether a div element is hovered.
The hook returns a ref to use with the div element and
a boolean indicating whether the div is hovered.
const useIsHovered = () => {
const ref = useRef < HTMLDivElement >
null;
const [isHovered, setIsHovered] =
useState(false);
// TODO : Rest of implementation
return [ref, isHovered];
};
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 126 / 140
Currently, TypeScript will not correctly infer the function
return type.
You can either fix this by explicitly typing the return type
like this:
const useIsHovered = ():
[RefObject<HTMLDivElement>, boolean] => {
// TODO : Rest of implementation
return [ref, isHovered];
};
Or you can use the as const trick to accurately type
the return values:
const useIsHovered = () => {
// TODO : Rest of implementation
return [ref, isHovered] as const;
};
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 127 / 140
➥ Tip # 89. Redux: Ensure proper typing by
referring to https://react-redux.js.org/using-
react-redux/usage-with-typescript to correctly
type your Redux state and helpers
I love using Redux to manage heavy client-side state.
It also works well with TypeScript.
You can find a great guide on how to use Redux with
TypeScript here.
➥ Tip # 90. Simplify your types with
ComponentType
Let's say you're designing an app like Figma (I know,
you're ambitious ). 😅
The app is made of widgets, each accepting a size .
To reuse logic, we can define a shared WidgetWrapper
component that takes a widget of type Widget defined
as follows:
interface Size {
width: number;
height: number;
}
interface Widget {
title: string;
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 128 / 140
Component: ComponentType<{ size: Size }>;
}
The WidgetWrapper component will render the widget
and pass the relevant size to it.
const WidgetWrapper = ({ widget }: {
widget: Widget }) => {
const { Component, title } = widget;
const { onClose, size, onResize } =
useGetProps(); //TODO: better name but you
get the idea 😅
return (
<Wrapper onClose={onClose} onResize=
{onResize}>
<Title>{title}</Title>
{/* We can render the component below
with the size */}
<Component size={size} />
</Wrapper>
);
};
➥ Tip # 91. Make your code more reusable with
TypeScript generics
If you're not using TypeScript generics, only two things
could be happening:
You are either writing very simple code or,
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 129 / 140
You are missing out 😅
TypeScript generics make your code more reusable and
flexible.
For example, let's say I have different items on a blog
(e.g., Post , Follower , etc.), and I want a generic list
component to display them.
export interface Post {
id: string;
title: string;
contents: string;
publicationDate: Date;
}
export interface User {
username: string;
}
export interface Follower extends User {
followingDate: Date;
}
Each list should be sortable.
There is a bad and a good way to do this.
❌ Bad: I create a single list component that accepts a
union of items.
This is bad because:
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 130 / 140
Every time a new item is added, the
functions/types must be updated.
The function is not entirely type-safe (see This
shouldn't happen comment).
This code depends on other files (e.g.:
FollowerItem , PostItem ).
Etc.
import { FollowerItem } from
"./FollowerItem";
import { PostItem } from "./PostItem";
import { Follower, Post } from "./types";
type ListItem =
| { type: "follower", follower: Follower
}
| { type: "post", post: Post };
function ListBad({
items,
title,
vertical = true,
ascending = true,
}: {
title: string,
items: ListItem[],
vertical?: boolean,
ascending?: boolean,
}) {
const sortedItems = [...items].sort((a,
b) => {
const sign = ascending ? 1 : -1;
return sign * compareItems(a, b);
});
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 131 / 140
return (
<div>
<h3 className="title">{title}</h3>
<div className={`list ${vertical ?
"vertical" : ""}`}>
{sortedItems.map((item) => (
<div key={getItemKey(item)}>
{renderItem(item)}</div>
))}
</div>
</div>
);
}
function compareItems(a: ListItem, b:
ListItem) {
if (a.type === "follower" && b.type ===
"follower") {
return (
a.follower.followingDate.getTime() -
b.follower.followingDate.getTime()
);
} else if (a.type == "post" && b.type ===
"post") {
return a.post.publicationDate.getTime()
- b.post.publicationDate.getTime();
} else {
// This shouldn't happen
return 0;
}
}
function getItemKey(item: ListItem) {
switch (item.type) {
case "follower":
return item.follower.username;
case "post":
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 132 / 140
return item.post.id;
}
}
function renderItem(item: ListItem) {
switch (item.type) {
case "follower":
return <FollowerItem follower=
{item.follower} />;
case "post":
return <PostItem post={item.post} />;
}
}
Instead, we can use TypeScript generics to create a
more reusable and type-safe list component.
I made an example in the sandbox below 👇.
🏖 https://codesandbox.io/p/devbox/generics-ts-
zvfp6n?file=%2Fsrc%2Fcomponents%2FList.tsx
➥ Tip # 92. Ensure precise typing with the
NoInfer utility type
Imagine you're developing a video game 🎮.
The game has multiple locations (e.g., LeynTir ,
Forin , Karin , etc.).
You want to create a function that teleports the player to
a new location.
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 133 / 140
function teleportPlayer<L extends string>(
position: Position,
locations: L[],
defaultLocation: L
): L {
// Teleport the player and return the
location
}
The function will be called this way:
const position = { x: 1, y: 2, z: 3 };
teleportPlayer(position, ["LeynTir",
"Forin", "Karin"], "Forin");
teleportPlayer(position, ["LeynTir",
"Karin"], "anythingCanGoHere"); // This ❌
will work, but it is wrong since
"anythingCanGoHere" shouldn't be a valid
location
The second example is invalid because
anythingCanGoHere is not a valid location.
However, TypeScript will not throw an error since it
inferred the type of L from the list and the default
location.
To fix this, use the NoInfer utility type.
function teleportPlayer<L extends string>(
position: Position,
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 134 / 140
locations: L[],
defaultLocation: NoInfer<L>
): NoInfer<L> {
// Teleport the player and return the
location
}
Now TypeScript will throw an error:
teleportPlayer(position, ["LeynTir",
"Karin"], "anythingCanGoHere"); // ❌
Error: Argument of type
'"anythingCanGoHere"' is not assignable to
parameter of type '"LeynTir" | "Karin"
Using the NoInfer utility type ensures that the default
location must be one of the valid locations provided in
the list, preventing invalid inputs.
➥ Tip # 93. E ortlessly type refs with the
ElementRef type helper
There is a hard and easy way to type refs.
The hard way is to remember the element's type name
and use it directly . 🤣
const ref = useRef<HTMLDivElement>(null);
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 135 / 140
The easy way is to use the ElementRef type helper.
This method is more straightforward since you should
already know the element's name.
const ref = useRef<ElementRef<"div">>
(null);
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 136 / 140
CATEGORY #13: MISCELLANEOUS
TIPS 🎉
➥ Tip # 94. Boost your code's quality and safety
with eslint-plugin-react and Prettier.
You can't be serious about React if you're not using
eslint-plugin-react . 😅
It helps you catch potential bugs and enforce best
practices.
So, make sure to install and configure it for your project.
You can also use Prettier to format your code
automatically and ensure your codebase is consistent.
➥ Tip # 95. Log and monitor your app with tools
like Sentry or Grafana Cloud Frontend
Observability.
You can't improve what you don't measure 📏.
If you're looking for a monitoring tool for your production
apps, check out Sentry or Grafana Cloud Frontend
Observability.
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 137 / 140
➥ Tip # 96. Start coding quickly with online IDEs
like Code Sandbox or Stackblitz
Local development environments can be a pain to set
up.
Especially as a beginner 🐣.
So start with online IDEs like Code Sandbox or
Stackblitz.
These tools allow you to start coding quickly without
worrying about setting up your environment.
➥ Tip # 97. Looking for advanced react skills?
Check out these books 👇
If you're looking for advanced React books 📚 , I would
recommend:
Advanced React by @adevnadia
Fluent React by @TejasKumar_
Building Large Scale Web Apps by @addyosmani
and @djirdehh
➥ Tip # 98. Prepping React interviews? Check
reactjs-interview-questions
React interviews ⚛ can be tricky.
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 138 / 140
Luckily, you can prep for them by checking this repo.
➥ Tip # 99. Learn React best practices from
experts like Nadia, Dan, Josh, Kent, etc.
If you want to stay up-to-date with the best practices
and learn tips, make sure to follow experts like:
@adevnadia: https://x.com/adevnadia for
advanced react tips
@joshwcomeau: https://x.com/joshwcomeau
@kentcdodds: https://x.com/kentcdodds
@mattpocockuk: https://x.com/mattpocockuk for
TypeScript tips
@TejasKumar*: https://x.com/TejasKumar*
@housecor: https://x.com/housecor
or me 😅
: https://x.com/_ndeyefatoudiop
➥ Tip # 100. Stay updated with the React
ecosystem by subscribing to newsletters like This
Week In React or ui.dev
React is a fast-moving ecosystem.
There are many tools, libraries, and best practices to
keep up with.
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 139 / 140
To stay updated, make sure to subscribe to newsletters
💌 like:
This Week In React by @sebastienlorber
ui.dev
Etc.
➥ Tip # 101. Engage with the React community
on platforms like r/reactjs
The React community is fantastic.
You can learn a lot from other developers and share your
knowledge.
So engage with the community on platforms like
r/reactjs.
Made with ❤ by Ndeye Fatou Diop
101 React Tips & Tricks 140 / 140
CONCLUSION
I hope this book has helped you learn some new React
tips and tricks . 🚀
I look forward to seeing what you build with React - feel
free to let me know how it's going on X/Twitter. You can
reach me at @_ndeyefatoudiop.
If you would like more tips/tricks, make sure to
subscribe to my newsletter, FrontendJoy.
A NOTE ON SHARING
If you'd like to share this book, I kindly ask that you
direct them to this link:
https://ndeyefatoudiop.gumroad.com/l/101-react-tips-
and-tricks.
This provides valuable analytics about how many people
are reading the book, and gives me a way to reach out to
readers directly.
This book is and always will be free to download ✨
Made with ❤ by Ndeye Fatou Diop