Video Summary and Transcription
This Talk explores the evolution of React components, starting from version .12. It discusses the introduction of class components and the deprecation of mixins. It highlights the emergence of higher-order components as a better option for code reuse. It also covers the introduction of hooks in React 16.8 and mentions potential future branches of evolution, such as server components and function components in hooks.
1. Introduction to React Evolution
Welcome to On the Origin of React. I'm Jen Craden, a senior software engineer at Netflix, here to talk about the evolution of components. React borrows from On the Origin of the Species, and just like Darwin's finches, React components have evolved and adapted. This talk provides context on older React structures and explains what's changed and why.
Hi y'all, welcome to On the Origin of React. I'm Jen Craden, and I'm a senior software engineer at Netflix, where I work on the Node.js Platform team. So, I'm here today to talk to you about evolution. Well, the evolution of components.
So, this talk borrows its title from On the Origin of the Species, one of the most well-known books in modern history. And when you see this book, or the name Charles Darwin, who wrote it, the word evolution probably comes to mind. Or maybe it's natural selection. Or if you're me, it's finches. I always think of Darwin's finches.
Now, all of these are fair associations, and actually all related. So, evolution is based on natural selection, and Darwin came to the idea of natural selection during his travels to the Galapagos Islands. Where he observed finches. All the same, but uniquely different. So, it's the beaks, each adapted to a specific food type. Seeing this graduation and diversity of structure in one small, intimate-related group of birds in this archipelago, one species had been taken and modified for different ends. Those are the finches. But this graduation and diversity of structure, this one species taken and modified for different ends, maybe that could be React components, too.
You see, we're at a point in React's history now where we can actually look back and see how it evolved. I promise you this isn't a history lesson. We're not gonna be memorizing dates or discussing the great RFC debates of 2016 or what have you. This talk is meant to be useful. Older versions of React are still in production. And depending on when you learned React, you might look at a component from a previous version and have some questions. That's where this talk comes in. It'll help you identify some of the older structures and provide context on what's changed and why. Now, if you've been writing React since the early days, this talk is more of a trip down memory lane. That's how I felt writing it. I've been working with React since version .12 and that was released in late 2014. At that point, React had been publicly available for over a year. Its initial public release was in mid-2013.
2. React Components Evolution
React components around version .12 marked the starting point. This period served as the baseline for component structure and behavior. React.createClass was used, as class and function components, as well as fragments, did not exist. Lifecycle methods, such as component will mount and did mount, remained consistent. Code reuse was achieved through mixins.
But I don't consider that our starting point. We're actually gonna start to look at React components around version .12. Again, this is when I started learning React and that's because React was actually becoming a thing. So it was starting to gain traction in the JavaScript community, React was the buzzword.
Also, there aren't significant changes to React components between .3 and .12. So this period of time is our baseline for how components were structured and behaved. And this is one of those components. Now, some of you have never seen this before and some of you are experiencing flashbacks, but yep, this is React in its early days.
Now, the first thing you probably notice is React.createClass. So at this time in React's history, class components don't exist. Neither do function components. We also don't have fragments and there are extra spans being rendered. That is true. And there is so much more. I'm not going to be able to list out all the differences in React and its ecosystem between then and now, but for a component structure, this is our earliest ancestor.
So as we start to build an evolutionary tree of React components, this becomes the base of the tree. All components from here out evolved from CreateClass. And those evolutions follow some common branches. So if we're looking at this component again, what attributes evolved along with component structure? Well, besides the creation of the component itself using CreateClass, what about lifecycle methods? Here I've stubbed out all the lifecycle methods available to us in React.12 and earlier. So that's component will mount, component did mount, will receive props, will update, did update, will unmount. Now, these are likely more familiar than CreateClass itself, and that's because these lifecycle methods do not change for a large part of React's history. You've probably seen these or worked with these life cycles at some point. Less familiar to you might be getInitialState and getDefaultProps, and these do exactly what you would expect, set the initial state or set the default props. So it's an unfamiliar structure but a familiar concept. Filling out the branches on our tree, life cycles become one of those branches. We're going to keep an eye out for how these life cycles evolve over time in tandem with component structure.
Now, there's one more branch on this evolutionary tree to explore, and that's code reuse. How do we share code between components? Well, back in the day of createClass, we used something called mixins. So here's a mixin. It looks remarkably similar to how we create components, but we don't pass this object to createClass.
3. React Components and Code Reuse
We're going to provide mixins to components for code reuse. Class components were introduced in version .13, bringing a fundamental shift in building React components. They extend from React.component instead of using createClass, and state is set in the constructor while DefaultProps is a class property. Class components don't support mixins, marking a slow start in evolving code reuse in React.
Instead we're going to provide it to a component with the mixins key and an array of mixins. So here I'm providing the countMixin to this component. And now I can use what's provided in the mixins as if it existed in the component itself. I'm using this.increment, part of the countMixin, and this.state.count, also part of the countMixin. So now we can fill in the code reuse branch of our tree with mixins as the first evolution in that branch. It doesn't take long, by the way, for this tree to grow.
So the next version of React builds on this base and we're going to start to see more familiar structures like class components. Class components are introduced in version .13. This is a big deal. So it's a fundamental shift in how we built React components. Now, createClass is still supported, but soon classes are going to become the default. And we're not going to spend too long looking at class components, but I want to point out a few key differences from createClass. The first being that we don't call createClass. Instead, we extend from React.component. Now, getInitialState and getDefaultProps are also no longer. We set state in the constructor and we set DefaultProps as a class property. Okay, so let's add class components to our tree. These are a direct evolution from createClass. Ah, but what about the other branches? Did anything change? In terms of life cycles, really nothing. As mentioned, initializing state and DefaultProps is a bit different, but the lifecycle methods themselves are still there. So, class components are still connected to the lifecycle branch. What about code reuse? Are we still using mixins with class components? No. So, class components don't support mixins. And this turns out to be the slow start of evolving code reuse in React. So, evolution tends to be a slow process. Natural selection happens generation to generation, small changes, adaptations allow species to survive and pass those traits on to the next generation. Now, peppered moths are a famous example of natural selection and action. Somehow over the course of 50 years, starting in the 1850s, the previously light colored moths changed colors. By the early 1900s, dark peppered moths were the dominant population. What's fascinating is why the moths changed colors, what else was happening during those 50 years? It wasn't the moths alone that were evolving.
4. React Evolution and the Deprecation of Mixins
The industrial revolution forced evolution on species. Mixins weren't deprecated in React until version .13, but class components without mixins pushed React to evolve past them. Function components were introduced as a simpler syntax for existing components, embraced by the React community. However, they lacked lifecycles and code reuse options. A blog post titled 'Mixins Considered Harmful' in 2016 signaled the upcoming deprecation of mixins in React.
The industrial revolution meant factories were built, factories that ran on coal that produced a dark smoke that coated the landscape, so light peppered moths were less able to camouflage themselves. Predators were able to spot them against the darker landscape, and their survival rate dropped, but the dark peppered moths, their survival rate increased. So sometimes natural selection is just sort of happenstance, a mutation that happens to be an improvement on the previous version, but sometimes evolution is forced on a species.
And I should be clear that forced doesn't imply intent. Mixins weren't even deprecated in version .13, and there wasn't intent to deprecate mixins in future versions. The rule was simply if a mixin is required, use create class. Class components were meant to evolve, react closer to idiomatic JavaScript. Mixins weren't part of JavaScript, so it made sense not to include them. Now, the lack of support wasn't meant to push React away from mixins, even though that is what happened. It wasn't intentional, but class components without mixins did force us to evolve past them.
Evolution is a slow process though, so before that happens, we have a new development in React. Stateless components, or as we refer to them now, function components. So stateless just wasn't the best term for these because components without state already existed. You could create a component with only the render function. Lifecycles, state, and even props are still 100% optional in React components. So it's better to describe these for what they are, function components. These are introduced as a new, simpler syntax for the components we were already writing, and I have to say, these are beautiful. When these are released, it is lovely. Even better with destructuring props, like, ooh, look at that, it's so gorgeous. When these are released, the React community fully embraces function components. These are a welcome addition to React's evolutionary tree.
Now, at the time, there are no life cycles in function components and no options for code reuse. Like class components, function components move React closer to idiomatic JavaScript. So this evolution makes sense. But now we have create class, class components, function components, and create class is still a requirement for mixins. That's gonna change soon. Actually, it's been in the works for a while. In early 2016, a new blog post is published to the official React blog. Its title is, Mixins Considered Harmful. Okay, what happened? So as of .13, mixins aren't deprecated and are gonna be deprecated, so what changed? Look, mixins weren't a great solution to begin with.
5. Evolution of Code Reuse in React
Mixins in React were initially used as an escape hatch but as React evolved and composition became popular, mixins were found to be harmful. Higher-order components (HOCs) emerged as a better option for code reuse, providing additional functionality to components. HOCs, although having some issues like naming collisions, offered a compositional approach to code reuse in React. With the introduction of higher-order components, React was on its way to becoming modern. React version 15 was skipped, and version 16 introduced fragments, error boundaries, portals, and more.
They suffer from problems with indirection, maintenance, and most importantly, breaking composition. Composition is a core React concept. Now, this blog post is still around if you wanna read it, but the too-long-to-read is. Mixins weren't meant for this. They were meant as an escape hatch. Three years have passed since React was released. The landscape has changed. Multiple Vue libraries now adopt a component model similar to React. Using composition over inheritance to build declarative user interfaces is no longer a novelty.
Mixins weren't considered harmful earlier in React's history but as React became more popular, as composition was adopted, as the users of React leaned into composition, we realized that mixins were harmful and that we didn't need the escape hatch they provided. There were better options that fit with React's compositional model, like higher-order components. So a higher-order component is a function that takes a component and returns a new component and that new component is often provided extra functionality so this function withCounter is a higher-order component and by the way, that is a mouthful so I'm gonna start to call them HOCs. This HOC takes in a component and provides a count and a function to increment that count.
So here is a button component that expects an increment function and a count and here I provide the button component to the HOC and I'm returned a new counter button with all the functionality that I need. This is a super contrived example but that's the gist. HOCs still have issues like naming collisions but we now have an option for code reuse with class components and it's compositional. Now we can add higher order components as the next generation of code reuse in React. I know it doesn't feel like it but with that addition, we're actually really close to modern React.
Closer than you might think. This tree actually doesn't change for a long time. React's next major release is version 15. Yeah, I know. We went from .14 to 15. Previous versions of React were meant to build to a stable 1.0 release but React was stable. And it had been for a while. So we just went to 15. And 15 has lots of updates but nothing that fundamentally changes component structure. So we're going to skip it and head to the next major release. Version 16. So its initial release, 16.0 introduces fragments, error boundaries, portals, and more.
6. React Evolution: Life Cycles and Hooks
16.3 and 16.8 introduced life cycle deprecations and hooks. Fiber, React's reconciler, allowed for pausing and resuming work, making some life cycle methods unsafe. New life cycle methods were introduced to handle these changes. Hooks are an evolution of function components, providing a new option for code reuse. The timeline ends with React 17, which has no new developer-facing features.
But no real changes to components or life cycles until a little bit later. So I refer to 16.3 and 16.8 as two major releases that weren't major releases. Technically, these are minor releases but these are when life cycle deprecations are introduced and hooks are available. So the life cycle deprecations are a bit jarring. Remember that React was introduced, that since React has been introduced, there have been no significant life cycle changes, but in 16.3, three life cycles are marked as unsafe. The life cycles themselves aren't removed, but to use them, you now you need to use the unsafe prefix.
And so this is for component will mount, will update and component will receive props. Now, why these changes and why now? The answer is Fiber. So Fiber is React's reconciler, and I'm gonna explain it to you very, very fast. If you're interested in this, there are tons of more in-depth resources, but to contextualize these life cycle changes, here's what you need to know. Before Fiber, the reconciler used recursion, but recursion doesn't allow you to stop and start a process. Fiber can, so React can do some work, pause, let the browser do some work, and then resume its own work, and so on and so forth. And because work can be paused and resumed, some life cycle methods were unsafe. We expect that component will mount will be followed by component will unmount, but with Fiber it's possible that component will mount could be called multiple times, and component will unmount wouldn't be called at all. So the work can actually be stopped before the component mounted, and that's why it's unsafe. To help with these changes, new life cycle methods were introduced. So that's get drive state from props, and get snapshot before update. I'm not gonna spend time explaining these, there's great documentation out there if these are unfamiliar to you, but these are the first new life cycles in all of react's history, and this is also the last evolution on that side of the tree. The side more centered around class components, life cycles, HOCs. As our timeline moves forward, hooks are introduced.
And again, I'm not gonna explain hooks, but these are an evolution of function components. So now we can use state and function components, and there are other hooks that allow us to hook into the capabilities previously scoped to class components alone. You'll notice though that I don't connect the lifecycle branch to hooks. And that's because the hooks aren't life cycles. There are no real life cycles in function components, that's the future of react. Use effect might run once, a cleanup function might be provided, but those aren't life cycles. Hooks are also a completely new option for code reuse that isn't even related to HOCs or mix-ins, it's a new branch all on its own. And just like that, we're at the end of the timeline. It came up fast, I know. 17 is released with no new developer facing features, and here we are at modern day React.
7. React Evolution: What's Next?
Our evolutionary tree remains unchanged. The React team's research on server components and function components in hooks could be potential branches for future evolution. As we move forward, we may forget how things used to be, but it's an exciting journey to see how far we've come. Thank you all for listening.
Our evolutionary tree remains unchanged. This might make us wonder, what's next? I have guesses, I mean, the React team shared their research on server components last year, that could be a branch on this tree. Or looking at function components in hooks, that part of the tree is kind of bare, so maybe that's where we'll see some evolution.
The truth is, I don't really know. What I do know is, in writing this talk, I realized how I'd forgotten just so much about early React. I guess that's the benefit of being able to move forward without looking back. And if you're new to React, it's gonna happen to you one day too, you're gonna forget how things used to be.
Oh, and I can't wait for that moment for you, because it wasn't just React evolving in that time, it was you too. And you're gonna get to see how far you came. And with that, I just wanna say, thank you all for listening. I'll see you online in one day in the real world too.
Comments