Now, before I get cancelled, let me explain đ I'm on the cusp of the boundary between Gen Y and Gen Z and I learnt React during the class component era. However, my stint with class components was only 2 years, which coincidentally is also the number of years between myself and the official cutoff of Gen Y. This made me think that - most of Gen Z would likely have learnt React from a point of functional components & hooks!
Consider this letter my reflections on the generational divide between the "Class Component Generation" and the "New Gen of React Devs," along with unsolicited coding tips for bridging that gap. And so really, the inclusive title is:
âA Love Letter to the New Gen of React Devs from the Class Component Gen of React Devsâ
My Dearest Gen Z / New Gen of React devs,
Whether from the bustling virtual classrooms of coding bootcamps to turning caffeine into code during late night coding sessions, we've both embarked upon our own unique journeys to learning React.
Yourâs began with functional components, where as for us, it was with class components.
When hooks stole our heartâs
In 2019, with the release of Hooks in React 16.8, the way we all learned React changed significantly. Prior to hooks, functional components were largely used for simple, stateless rendering, while class components handled the more complex logic, state management, and lifecycle methods. However, with the introduction of hooks, functional components gained the ability to manage state and side effects and it altered the way React applications were built.
Justifiably, the hooks docs assumed the reader was familiar with class components. However, those learning React post-2018 likely didn't prioritise learning class components, as the React ecosystem was shifting towards a functional component approach, leaving little incentive to learn the "old way." This caused some:
Unintended Differences (but, Differences make the heart grow fonder)
As a result of your learning journey, there are some notable "generational differences", highlighted in the quotes below, in your approach to React development compared to ours. Let us explain each difference and how it came to be:
1. âYour generation didnât learn the life cycle methodsâ
Explanation: This is the most significant difference. For us, the class components generation, managing state meant we were forced to understand the nuances of the lifecycle methods (componentDidMount()
, componentDidUpdate()
, and componentWillUnmount()
). These methods provided clear entry points for initialising, updating, and cleaning up state, that hooks abstracted away.
2. âYour generation tend to jump to state management librariesâ
Explanation: Prior to hooks, integrating Redux into a React application was a PAIN. It required a good understanding of the flux architecture and oh-so-much boilerplate code. So even though, with hooks, the process of integrating Redux (or rather Redux Toolkit) has become much easier, the scars of past serve as a reminder of the potential complexities making us less likely to reach for a state management library.
3. âYour generation doesnât like using TypeScriptâ
Explanation: Class components provided an object-oriented programming (OOP) approach to React, which aligns well with the typed nature of TypeScript. Consequently, we are more inclined to TypeScript as a means of enforcing code maintainability and improving the type safety that was lost when we switched to functional components.
4. âYour generation love using hooksâ
Explanation: Class components had specific lifecycle methods like shouldComponentUpdate
that allowed for manual control over component re-rendering, encouraging developers to think about performance optimisation. With hooks like useCallback
and useMemo
, the optimisation techniques are not as explicit or intuitive, making it harder to identify the most effective use of these hooks. Similarly, because class components required a deeper understanding of the component lifecycle and how it relates to rendering and updating we were able to understand when to touch the DOM when using useRef()
.
5. âYour generation take testing for grantedâ
Explanation: (this one is probably my most opinionated) The step-by-step nature of class components encouraged testing practices, as we had to address each component's lifecycle events and behaviours individually. Additionally, class components required more explicit setup for testing, such as mocking lifecycle methods. Comparatively, testing functional components is much âeasierâ now, so for us its a âno brainerâ, while for you it may seem like an afterthought.
So I guess what iâm trying to say is, we may have been too quick to judge your coding choices and a little too harsh in our code reviews. But upon reflection, it's clear that our different learning journeys have shaped our approach to React development. And just as we wouldn't expect you to understand our love for flip phones and skinny jeans, it's unfair to expect you to have the same understanding of class components and lifecycle methods.
So now, instead of dwelling on our differences, let's focus on how we can bridge the gap and learn from each other.
Instead of criticism, allow us to offer: React Love Advice
Dont carry unneccessary baggage: Avoid storing unneeded state (âYour generation didnât learn the life cycle methodsâ)
- We learned the hard way, that unnecessary state can quickly lead to complexity and performance issues. Avoid storing state that can be calculated from other state values, prop values, or isn't essential.
- Enforce Immutability, don't transform data in state â transform that data while rendering instead
- Watch out for duplicated or deeply nested state.
Master the love language of hooks! (âYour generation love using hooksâ)
- Managing the component lifecycle explicitly taught us the importance of separating concern.
useEffect
may seem like a simple way to handle side effects and state updates, however weâve learned that multiple useEffect calls with many dependencies can quickly lead to spaghetti code and performance issues. - On the flip side, watch out for missing dependencies in useEffects as this can lead to unexpected behaviour in your application.
- Make use of the cleanup function returned by useEffect to avoid memory leaks and properly handle component unmounts.
- Don't overuse hooks like
useMemo
anduseCallback
, as premature optimisation, as this can have the opposite effect to its intended purpose due the the overhead of these hooks - Theres a great talk on using hooks: using useEffect Effectively by David Khourshid or 12 useState & useEffect Mistakes Junior React Developers Still Make in 2024. by ByteGrad
TypeScripting our way to a stronger relationship! (âYour generation donât like using TypeScriptâ)
- TypeScript improves type safety, maintainability, and scalability â all crucial as your applications grow!
- Many popular libraries and frameworks (e.g. React Native) are now going down the TypeScript as a default route.
Managing Our Love State (Management) (âYour generation jump to state management librariesâ)
- Understand when to use a State Management Library, only reach for Redux or similar libraries when you have a clear use case.
- At the same time donât stick everything into one Context, break down your state into smaller, more manageable contexts that are specific to different parts of your application. This is a great article from Kent C Dodds.
- For managing asynchronous operations, consider a library like TanStack Query.
Nurturing Our Love with Tests (âYour generation take testing for grantedâ)
- Keep components focused on a single responsibility. Refactor complex logic into custom hooks or utility functions. A good reference is the book Clean Code by Robert Martin.
- Make sure your component isnât too large, coupled or nested and think about the dependency flow.
- Follow principles like unidirectional data flow, atomic design, and decoupling components.
- Favour composability over inheritance. I really like this page from the legacy react docs.
- Post hooks, the project structure of React changed (e.g. no more container). Consider folders for hooks, context, and types and ensure you are thinking about the core principles of readability, maintainability, and reusability from the star. This will save you refactoring headaches down the road.
- Pass dependencies into a function or component from the outside, rather than having the function or component create or manage its dependencies internally (Dependency Injection).
In the future, we will get through this together
So now as we venture into the unknown world of React 19 with its Server Components and the new React Compiler. We can share notes and ponder together on whether the compiler will ease the pains of useMemo(), useCallback() or if we are going to have to work together to write an article for the post-compiler generation đ
With all my love,
Gen Y / The Class Component Generation of React Developers
Massive thank-you to Joe Brady, Charlotte Isambart, Justin Zelinsky, Justin Kek, @hellonehha and Antony Clements who fed into the Lessons Learnt
Please drop any comments of other lessons learnt / your experience!