React v16.3.0: New lifecycles and context API
reactjs.org
reactjs.org
* Context overhaul that makes it way easier to pass state/props down multiple levels of components. Uses function as a child for the API
* Adds React.createRef function to create refs. Creating refs through callbacks is still a thing for advanced cases, but this provides a more ergonomic API to replace the old clunky ref={(c) => this._yourthing = c} type callback.
* Adds React.forwardRef, which solves the issue of HOC not passing refs to the component they're wrapping.
* Lifecycle methods (componentWillMount, componentWillReceiveProps, and componentWillUpdate) are now considered legacy, and will be deprecated in future releases. To replace them, they added two new ones (getDerivedStateFromProps and getSnapshotBeforeUpdate). This is due to these lifecycle methods interfering with the goals of async rendering and other ambitious ideas.
* Adds a StrictMode component, which basically just causes React to log more errors during development about deprecated/legacy APIs and unexpected side effects.
1. you need to set the display name
2. you need to set the wrapped component
3. you need to hoist statics
4. and with this update, you'd forward refs
Granted, I could just write it myself real quick, but it'd be nice to just have an API you could use that doesn't require you to fully grasp all the complications that come with using a HOC. I've recently just deferred to using render props instead of HOCs. Render props also require way less complex type definitions if you're using flow or typescript.
[0]: https://github.com/ReactTraining/react-router/blob/master/pa...
There are still rarer cases where callback refs are useful. For example when you need to run a side effect on a node as it attaches and detaches.
Also note: legacy lifecycles are not deprecated yet. You won’t see the warnings unless you opt in with <StrictMode>. They will be officially deprecated in a future minor release when more libraries have had time to update.
You asked "Curious, what do you use it for?" in regard to componentWillReceiveProps
https://news.ycombinator.com/item?id=16695064
I use componentWillReceiveProps for just about everything - I had thought it was a fundamental part of the Redux flow. i.e. Redux updates, new props flow through the hierarchy, I pick that up in componentWillReceiveProps and make changes based on new props.
Is this not correct?
What you're describing sounds more like you're updating something external (e.g. your Redux store). This isn't what the lifecycle is meant for, and you'd be better off using componentDidUpdate instead.
While that’s a little tongue in cheek, the lifecycle method names I would rate as a drastic drop in intuitiveness:
Previous:
componentWillMount
componentWillReceiveProps
componentWillUpdate
New: getDerivedStateFromProps
getSnapshotBeforeUpdateYou still have `componentDidMount`, `componentDidUpdate`, and `componentWillUnmount` that should be used more commonly and do exactly what you expect them to.
Thanks to you and brianvaughn for answering so many questions.
The biggest one is probably adding event listeners or setting up subscriptions (which can cause memory leaks).
Here are some others: https://github.com/reactjs/rfcs/blob/master/text/0006-static...
> Is it correct to say that `getSnapshotBeforeUpdate` is named to indicate what you were supposed to use `componentWillMount` for?
No. `getSnapshotBeforeUpdate` is not really related to `componentWillMount`. It relates more to what people were using `componentWillUpdate` for.
Of the big JS frameworks that are popular at the moment, I think the React team is doing the best job of balancing new features with minimizing developer pain and breaking changes.
If I had one thing negative to say about this release, it's that I think the forwardRef is maybe moving in the wrong direction a bit. In my experience, higher order components should be the exception, not the rule. They are a neat way to provide some functionality that is otherwise difficult, but their cognitive and maintenance overhead are high. If it stops at forwardRef, no big deal, but I am not sure HOCs are something that deserve explicit API support (and the additional surface area that entails)
React has a great deprecation strategy. Their general philosophy is "if you can use version X with no warnings, you should be able to use version X+1 with no changes to your app."
I'm happy to see a thoughtful solution. There are some things you just can't do with React alone (like listen for PointerEvents).
https://developer.mozilla.org/en-US/docs/Web/API/PointerEven...
If anything, I think the new context API (and even forwardRef itself) are endorsements of the render prop pattern more than of HOCs. :)
This just isn’t true. Abstracting form logic. Request logic. Anything else you use across your app. Connect in redux. There are hundreds of good reasons to have a higher order component.
It makes testing easy. Test your request logic in one place, anything that uses it is now a stateless component, and testing it is trivial.
Request logic doesn’t belong in a render function. Routing definitions don’t belong in a render function and neither does abstract form logic.
From my experience of talking to React developers, data fetching is the most common problem people bump into with React apps. Async rendering is key to making data fetching easy and natural in React, and to providing a great user experience whether the user is on a fast or a slow network. Please watch the second part of my talk for a demo if you’re not convinced: https://reactjs.org/blog/2018/03/01/sneak-peek-beyond-react-...
>We’re losing some important escape hatches
We believe that the combination of new lifecycle hooks and the non-legacy old ones covers all use cases we are aware of (including escape hatches!). If we missed some use case you relied on, we are asking you to let us know: https://reactjs.org/blog/2018/03/27/update-on-async-renderin...
Escape hatches are core to React’s philosophy. To reiterate a point from the blog post, we have more than 50,000 React components at Facebook, and they aren’t going to rewrite themselves :-).
So we are committed to both supporting those lifecycles under `UNSAFE_` aliases (as mentioned in the blog post, they will continue working in React 17), and will provide async-safe hooks for use cases we might have missed (please report them!)
Thanks.
I really hope you will hold off deprecation warnings until 17.0.0 instead of 16.x, even if that means you need one more major version than you had planned. Being on a team whose policy is to not shrinkwrap, so that we automatically get patches (not my policy, personally), at some point we’re going to start seeing all these deprecation warnings without having explicitly version-bumped React.
My overall concern is that we’re paying for the async renderer by moving to a higher level of abstraction for all class components. With Fiber, React is becoming a vastly different library. From the outside, having seen Fiber slated for release in 16.0.0 and, being pushed back to an unknown 16.x release, and then being pushed back to 17.0.0, it seems like you had not anticipated the extent to which the core React API had to be changed to enable the async renderer. It seemed simple enough in the beginning, but now we’re looking forward to losing what React once was so we can use a DOM renderer that will be optional anyway.
"fiber" was the code name for the rewrite that was released as version 16. Async rendering is a feature that we've been working on adding- (which the "fiber" rewrite was done to support). We've known that would take some time, mostly due to the fact that we want to gradually migrate old apps (as much as possible). :)
We're also working on other cool, related efforts- like a compiler (https://twitter.com/trueadm/status/944908776896978946) and proper built-in support for async-data, dubbed "suspense" (https://reactjs.org/blog/2018/03/01/sneak-peek-beyond-react-...).
That sounds like it could bring a lot of benefits. Would, and if so how, that affect people using TypeScript?
Async rendering indeed needed more research but we’ve been testing it internally for months now, and are getting closer to the point where it’s ready. I disagree with you that replacing a few often misunderstood lifecycle hooks with more concrete alternatives significantly raises the abstraction level.
We did anticipate that some lifecycle methods will be problematic (e.g. https://github.com/facebook/react/issues/7671 was filed over a year ago, or a few months before the first line of Fiber code was written).
We’ve thought about these issues for quite a while. Jordan (creator of React) started thinking about them about four years ago (https://twitter.com/jordwalke/status/500587022890061824).
We needed time to evaluate different possible migration strategies and experiment with them, and we have a good idea now of what they look like. We don’t have all the answers yet but we’re getting there, and are doing that in the open.
For the rare cases where you do want to retain previous props, yes, we’re asking you to be explicit about it. This will allow better memory usage in future versions of React, and also avoids the need for an extra `prevProps !== null` check that would need to happen in every `getDerivedStateFromProps` method.
How many components do you have? Absolute number doesn’t tell me a lot. :-)
>Most cases are for detecting changes requiring freeing/fetching network based resources where the data involved can get excessive if care is not taken.
Not sure I fully understand. Could you provide a small example?
A form of master/detail is driving the need to detect prop changes to fetch additional data while freeing previous data. Consider the master/detail of multiple levels deep driven by route parameters through the ui hierarchy. In RN, this is less of an issue since screens tend to be more focused and ui stack driven. But users working full-time in a complex ui specialized for power users can require much more data/dynamism.
And thanks for listening about my use case. One concern about using a static function for getDerivedStateFromProps() is that it will not allow for taking instance variables into account. Have not thought through how that might come into play but components certainly allow for this, though many may feel it a strange idea. Perhaps a reference to the component could be passed as well as an additional argument.
I look forward to coming up for air and catching up with the improved approach. Things continue to move quickly and I continue to be impressed at how the changes bring improvement to the ecosystem.
The other case of componentWillReceiveProps usage I see is where there is computationally expensive derived state, but for some reason the asynchronous nature of setState prevented it from being used, and an instance variable "had" to be used instead. There are a few cases of these where after much hair pulling the instance variable "fixed" the issue.
Sounds like it, based on your description!
> The other case of componentWillReceiveProps usage I see is where there is computationally expensive derived state, but for some reason the asynchronous nature of setState prevented it from being used, and an instance variable "had" to be used instead.
`setState` calls made from within `componentWillReceiveProps` are processed (synchronously) before `componentWillUpdate` or `render` are called.
https://reactjs.org/blog/2018/03/27/update-on-async-renderin...
(TLDR: Use `componentDidUpdate` for this.)
Have you had a chance to skim through it?
Since I switched to hyperdom[0] I haven't missed lifecycles at all. In fact my app has practically none of the "plumbing" code that my old react apps seemed to have.
Hyperdom is so much simpler but seems to be just as powerful.
I've never used Hyperdom, so this isn't meant as a criticism or commentary on it.
They'll clean up, grandma, aunty and the school mothers will continue posting and they never even knew there was an issue.
People will soon forget.
https://www.theguardian.com/technology/2007/feb/08/business....
Having 2 billion users is not a 1000x better than having 2 million ones. It's more like a million times. The network effect compounds non-linearly.
Facebook (the company) is here to stay. Even if the product is gone, they own 2 other multibillion dollar social networks: Instagram and WhatsApp.
While it may be possible that FB fails, there’s not a whole lot of symptoms apparent that it will, unlike MySpace.
I hear people TALK a lot about Vue. Nothing against it, I enjoyed my little hobby project in it. But way more people talk about using Vue than actually using it. In no way is it "eating Angular's lunch". This is coming from someone quite critical of Angular.
Every time I read of a new release I get this chill down my spine for a second.
To the parent's parent's point Javascript has evolved since 2015 and comparing what a 2015 app looks like to a 2018 app is day and night.
I did not mean to imply that you would need to step through every minor version though. I was just saying that- if you were using e.g. 15, update to the latest 15 release (15.6.2) and fixing warnings in it before updating to 16.
I tried to explain the client that I needed to upgrade the code before even being able to understand what the problem was, and I lost the client.
As much as they suck, PHP apps from 10 years ago are still working great, and so are jQuery-based apps from 5 years ago.
Would never go back to either one, though.
Don't forget that a React project might be made of many libraries--not just React itself--that might be worse at supporting legacy versions and guaranteeing mooth upgrades.
Don't forget that a React project might be made of many libraries--not just React itself--that might be worse at supporting legacy versions and guaranteeing mooth upgrades.
For this reason, jQuery-based projects from 3-4 years ago still work completely fine (including jQuery plugins), and I would imagine will keep working for years to come.
React is continuously evolving, and 1 year is enough to make React code and/or code in plugins used in a React project obsolete. Because of this, React projects break more easily if one doesn't or can't keep upgrading the code (for instance, because it was a client you don't work with anymore).
This is not good or bad, or anyone's fault, it's just how it is.
Like I said in previous comments, I would never go back to writing PHP or jQuery apps, but this is definitely an issue--either intrinsic of using an evolving technology like React, or caused by relying on too many plugins from authors that don't have the resources to keep supporting legacy APIs or upgrade their plugins to using the new ones.
As for the "boilerplate" topic: you are encouraged to use as little or as much abstraction on top of Redux as you want, whether it be small utility functions or entire other frameworks that wrap Redux. We've got a "Reducing Boilerplate" page in the docs [1], and I've got a section of my React/Redux links list for articles on that topic [2].
I'd be happy to suggest possible solutions for whatever problems you're concerned about.
[0] http://blog.isquaredsoftware.com/2018/03/redux-not-dead-yet/
[1] https://redux.js.org/recipes/reducing-boilerplate
[2] https://github.com/markerikson/react-redux-links/blob/master...
Not if you use a HOC (e.g. `withContext(Component)`) like a couple of the docs examples showed.
> Additionally, the semantics of having a consumer's props.children be an immediately invoked function are a little bit unintuitive to me.
That's fair! I would encourage you to give it a chance though. I think it's a pattern that really grows on you after a short time. :)
IsolatedComponents wouldn't inherit props from their parents. The idea of an IsolatedComponent would be to present a boundary on inherited props. That would give you the simplicity of the old context api, but with more fine-grained control over how it is propagated (hopefully leading to fewer foot gun moments).
Say context doesn't exist, and you have a branch of components A -> B -> C, if C depends on some state in A, B would have to have a dependency on that state too. And you'd need some boilerplate code in B that reads the props from A, and passes them to C.
If you want to use C in a different context, say A -> D -> C, then that boilerplate code needs to exist in D as well.
With larger apps, containing large component trees, the amount of this boilerplate prop-passing you need to do starts to get excessive. It's not uncommon to see components read 14 props from their parent, use one of them, and pass 13 to their child(ren).
Until now, this problem has been solved by using data stores that inject state anywhere in the tree that they please. Context is an alternative to that.
It's always existed, just now it has a new API. I have no idea what was wrong with the old one. Nobody seems to have properly explained it anywhere, the docs just make condescending suggestions not to use it without reasoning. I think I saw an arcane tweet once explaining that certain things may not have re-rendered properly when using the old API or something...
I still think I'll need examples to be convinced of the usefulness, though. I don't see why a large component tree would have 14 disparate props passed down individually. If the tree is supposed to be a reusable component, why not define a single object to pass down?
Edit: Having read the docs I now see that my current perspective on it is the one they want me to have. "Don’t use context just to avoid passing props a few levels down. Stick to cases where the same data needs to accessed in many components at multiple levels." So it's better for me to avoid it until I see it's definitely needed.
I know there are several ways to construct a component, but I like that your examples make them functions of props, because that's how I picture a component. Having its behavior depend on a semi-hidden context appears less purely functional. To stretch the term a little, the component is no longer idempotent. Maybe there's a situation where that's what you want, but the examples you've given so far sound like ones where props would serve just fine.
Just in case there's any misunderstanding- (I don't think there is, but just being safe)- context isn't implicit. You have to opt into it either by using the `<Consumer>` component or a HOC that injects a context prop.
For example if I'm looking into a new library that claims to simplify my app's architecture or reduce boilerplate, I need to see what the alternative would look like for a given example, otherwise it's just a baseless claim.
Same as things that purportedly "scale". Since every library under the sun claims that as a benefit, I wouldn't even bother recommending any of them to anyone on here without an example of a large app both with and without the library, pointing out the specific sections of code that change for the better.
- It was a single shared key/value namespace, which could potentially lead to different libraries attempting to put values at the same key and stomping on each other
- It was fine for passing down _initial_ values, but if you tried to _update_ a value, any components that returned `false` from `shouldComponentUpdate` would block their descendants from receiving the updated context values
https://reactjs.org/blog/2018/03/29/react-v-16-3.html#forwar...
Instead, you have islands in the vdom tree that share a single, common state object.
It seems like react gets re-written and APIs undergoes drastic changes every few months. I'm not complaining about churn (I don't write react), but rather that maybe React isn't the right tool to be hyping up, or pointing newcomers to JS at to look into/use.
I don't remember any other supposedly simple library having had this many major rewrites/api changes. I don't think I've ever seen something like this in KnockoutJS or even Angular1 for a long time (even though the way you had to handle $scope and apply/digest cycles was terrible). Relative newcomers like Mithril and Vue seem to get this stuff right without requiring as much breaking changes. Maybe they owe their simplified structures to some of the new thought to what React inspired, but it seems like they're actually simple (Vue's documentation is amazing, concise, and you can actually render Vue in the browser to get started quickly).
Maybe it's time to rethink whether React is a good tool. There's no way I would recommend React to anyone (beginner or intermediate) over something like Vue these days.
For example, I don't know what HOC (and am thoroughly uninterested, it sounds like the same kind of thing you only need to know when you're chest-deep in React land) is but it sounds like a hack necessary because of a bad design decision.
[EDIT] - Took a few seconds to look up HOCs (Higher Order Components) and they're a bad example of what I think is incidental complexity inside React but I think my point still stands. To clarify, here's my thesis:
React is complex (both internally, evidenced by multiple rewrites, and with the tooling it forces you to use, evidenced by needing to download a zip file to start, or touch webpack), but masquerades as a simple library.
When we need to make changes, it’s the team of 8 people that need to port all those dozens of thousands of components to new APIs. It would be infeasible for us to break APIs every few months, or to do so in a manual way. This is why we embrace automatic codemod scripts.
There have been some breaking changes between major releases (which have a roughly yearly—not monthly—cadence) but we always provided automatic scripts that convert your code as much of your code as possible automatically: https://github.com/reactjs/react-codemod.
We also call attention to the automated migration scripts in all major release blog posts (for example: https://reactjs.org/blog/2017/09/26/react-v16.0.html#upgradi...).
That’s the only way we could maintain dozens of thousands of components while still moving the library forward.
I hope this information is helpful. You can read more about our commitment to stability without stagnation here: https://reactjs.org/docs/design-principles.html#stability
I understand the team is hard at work making React better, and there is absolutely no doubt that you are succeeding and react is getting better. However, the amount of large internal changes indicates that as you improve and (most likely) shed unneeded complexity, that there must have been complexity to shed. Obviously no one is perfect, and hindsight is 20/20, but for such a "small" (yes, interacting with the dom is actually super duper hard, and edgecases abound) scope, I've seen more large-ish overhauls than I am comfortable with.
Many internal changes makes me feel like react is more Mongo than RethinkDB (the latter being a document store that actually got most things right and was well engineered but no one ever heard of while mongo started with a badly engineered product, good marketing, and pivoted to relative reliability and good engineering).
I want to also note that I'm not in the target audience for a post like this. There are tons of people who lovingly and productively use React -- I'm not one of them. I am just dumbfounded when people bill react as simple but have never heard of something like Mithril that actually is simple (almost "stupidly" so), and another post at the top of HN means there's that many more people I have to convince to pick something at least slightly simpler when they start doing web development for the first time.
The v16 rewrite (dubbed "fiber") wasn't just about cleaning up internals- it was about supporting a fundamentally different type of behavior- asynchronous rendering [1]
> Obviously no one is perfect, and hindsight is 20/20, but for such a "small" (yes, interacting with the dom is actually super duper hard, and edgecases abound) scope, I've seen more large-ish overhauls than I am comfortable with.
To be fair, React supports many more targets than just the DOM. In addition to the DOM- there's react-native, react-art, react-music, react-hardware, etc.
Don't mean to sound argumentative, just wanted to add some info in case there were potential misconceptions. :)
1: https://reactjs.org/blog/2018/03/01/sneak-peek-beyond-react-...
You're absolutely not being argumentative -- while I still don't think react is the simplest thing to start with, the explanations that have been given so far seem very very reasonable, feel like an asshole for essentially ranting
We‘ve lived with the same codebase for four years and frankly, it was slowing us down. Not all original abstractions aged well because when React was written, we didn’t really have a lot of experience with React :-)
I don’t think it’s because the original code was bad (I’m not familiar with Mongo so I don't know if that’s what you were implying), but because it’s not very easy to come up with an architecture that is both efficient and targets multiple view output targets (e.g. React Native) from the first attempt. Especially considering support for Native was retrofitted into an existing design at some point. It was also pretty much impossible to foresee the kind of features we’d be working on in a few years before anybody used React.
Some fundamental assumptions in the old codebase were actively preventing us from implementing new features. For example, there was an assumption all over the codebase that a custom component always renders one root, but this prevented us from letting people return arrays from render. There were many small things like this, and at some point it was clear we needed to redesign the internals from scratch.
The Fiber rewrite was about unlocking our larger vision (we recently shared its details: https://reactjs.org/blog/2018/03/01/sneak-peek-beyond-react-...) but it was just as essential for smaller incremental improvements.
Long-requested features that shipped in React 16 like fragments (returning multiple components from render), error boundaries (gracefully recovering from runtime errors), portals (declaratively rendering the child React tree to a different DOM subtree), and an official context API (finally shipped today) would not be possible without rewriting the internals to an architecture that incorporates our learnings about how React should work that we’ve accumulated over the years.
I’d also argue those features (and then ones we’re working towards—please do watch my demo at the previous link!) are a bit more ambitious than “interacting with the DOM”.
> am just dumbfounded when people bill react as simple but have never heard of something like Mithril that actually is simple
I agree React is not simple internally, but that was never the goal. The goal is to make it simple to build products with React, and so far I believe its success speaks to product developers finding it helpful.
I don’t push for React being used everywhere (and I heard people who used Mithril are happy with it) but I hope this gives some insight into our decision and development process.
We didn’t rewrite React because we wanted to clean a few things up, or because we didn’t know what we were doing. We’re pursuing very specific goals, and a new architecture is essential to enabling them. And there were no multiple rewrites; just one: https://code.facebook.com/posts/1716776591680069/react-16-a-....
But hearing what was being dealt with (those blog posts) definitely clears things up for me -- Also as the other commenter mentioned, react is aiming for so much more than the DOM (react native was a visionary/revolutionary achievement IMO, not even trying to blow smoke, I think at this point that's pretty much a fact) it makes sense why and how react has grown the way it has.
Thanks for taking time to clear up my misunderstanding/where I was wrong
I think it's a great library and has only really gotten simpler and better. But, to each their own.
My problem is that it was being marketed (heavily) as simple and amazing at the beginning when it simply wasn't. From what I remember, the FB team was explicit about it not being for beginners, but the amount of marketing and trumpeting made that basically impossible, and now bootcamps are teaching it to beginners.
I just am super tired of having to talk people out of using it on their first project after learning and using HTML+CSS for the first time. I always sound like the insane person, saying "react is complicated" when all the marketing and blogs and everything else is saying "react is simple".
const Age = (props) => <div>{props.age}</div>
const App = () => <div><Name name={'Zed'}/><Age age={20} /></div>
Is about as simple as it gets. This sort of composability can go _very_ far without needing any of the other API methods.
This isn't a benefit of react though -- this is just react incorporating a well known functional paradigm, which I don't mind.
IMO If react was simple, this would have been the way to do it from the start -- no mixins detour. This answer would have fallen out (as it now has), if they simply didn't offer a way to inherit functionality, in that manner. If a component is truly just a function from state to output, the obvious way to combine them IS higher order functions.
Also, I'm not sure the characterization as a factory is correct, seems closer to higher order functions (at the very least name wise), and/or enhancing features by composition.
My point is that it's not that simple (for better or for worse, some of the added complexity in the API IS essential IMO), but people still say things like "components are just functions" like it really is that simple.
For example: https://reactjs.org/docs/react-component.html#static-getderi...
This note:
> Note that if a parent component causes your component to re-render, this method will be called even if props have not changed. You may want to compare new and previous values if you only want to handle changes.
Things like that are indications of hidden complexity, in my experience.
So if anything, this particular example speaks more to React components being “just functions” (which is a statement I disagree with—but I don’t think this example is very relevant to that).
(And yes, that's a very good post.)