* 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.
I'm curious that with the addition of APIs like forwardRef, if it wouldn't make sense to just add some HOC helper that does the HOC ceremony for you. For example, if you look at withRouter[0] from react-router
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.
Note: createRef doesn’t replace callback refs. It just offers a more ergonomic API for common use cases.
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.
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.
The purpose of componentWillReceiveProps was to update state in response to props changes. (This is also the purpose of getDerivedStateFromProps.)
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.
This is intentional because those are relatively rare use cases. We want them to stand out and be quite specific about what they're doing.
You still have `componentDidMount`, `componentDidUpdate`, and `componentWillUnmount` that should be used more commonly and do exactly what you expect them to.
In your experience, what are the kind of things that people do in `componentWillMount` that they shouldn't be doing? Is it correct to say that `getSnapshotBeforeUpdate` is named to indicate what you were supposed to use `componentWillMount` for?
Thanks to you and brianvaughn for answering so many questions.
I like that they are taking the deprecation of the lifecycle methods slowly. And the automated script to migrate to the 'UNSAFE' version of those methods in 17 is a nice touch. I don't know if I love the new context API, but it's great they're giving an official option.
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)
> I like that they are taking the deprecation of the lifecycle methods slowly.
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."
Using refs to talk to the DOM is a leaky abstraction, but talking the the DOM is a necessity in a framework that renders to the DOM. Before forwardRef, you'd end up with ad-hoc prop names like `domRef` to try to wallpaper around the leak.
I'm happy to see a thoughtful solution. There are some things you just can't do with React alone (like listen for PointerEvents).
“higher order components should be the exception, not the rule.”
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.
No, they should still be the exception for the reasons already stated but also because defining all logic in a “render” function means it’s tightly coupled to your view logic, which you do not want.
Request logic doesn’t belong in a render function. Routing definitions don’t belong in a render function and neither does abstract form logic.
These lifecycle changes make me nervous. We’re losing some important escape hatches to make way for async rendering which solves a problem that very few web apps have. To use an analogy, it’s like “unsafe” features being deprecated in C to make way for the next major release which will be more like Java. My first impression of this is that it’s the beginning of the end for React. People can still use old versions, but the reality is that they’ll have to stick with old versions of related libraries and they’ll have to shrinkwrap their node_modules because these drastic changes are being rolled out in minor version updates.
>async rendering which solves a problem that very few web apps have.
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!)
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.
> 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.
"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). :)
I don't think we know enough about where compilation will end up to comment much. I wouldn't expect TypeScript to be negatively affected though (since it compiles to JavaScript). So far, the problems we're noticing are from certain coding patterns or practices within components. However we deal with these (whether we add new lifecycles or encourage alternate patterns, etc) it should hopefully apply equally to JS/Flow/TypeScript.
There was never a plan to enable async rendering in React 16. “Fiber” referred to a new internal architecture (which enabled fragments, error boundaries, portals, and now context). It was a prerequisite to exploring async rendering, but it was also important for the above features, and it shipped in React 16.0.
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 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 what it’s worth, I think the async rendering changes/data fetching within components is a lovely idea. But I’m biased and desperately want reducerComponent from ReasonReact in Flow/React...
Does anyone finds the new getDerivedStateFromProps adds too much boilerplate code for the developer to write and be aware? So for every previous prop that I need to compare with I need to remember to update it in the state? If before it was as simple as this.props.X!==nextProps.X now I need to set the state with the current prop!
This lifecycle exists for a fairly rare use case. Why do you use it often? Instead of keeping state “in sync” with the props, can you just read it from the props?
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.
Responding to "exists for a fairly rare use case" statement, I just searched the the web app I am currently writing and it uses componentWillReceiveProps() ~20 times. Most cases are for detecting changes requiring freeing/fetching network based resources where the data involved can get excessive if care is not taken. This would seem to be a common scenario for many domains. On a sidenote, I have thoroughly enjoyed working with react and RN over the last 4 years and have learned much from dan's work. Thanks guys.
>I just searched the the web app I am currently writing and it uses componentWillReceiveProps() ~20 times.
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?
Component count is around 140. Fwiw, some are stateless, functional components while others are more complex.
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.
Fair enough. As mentioned in Update on Async Rendering blog post, there’s a less verbose data fetching API coming down the line (“suspense”). Not quite ready for the release yet. But it will require components to be async-compatible which is why we're getting these lifecycle changes out now.
Intriguing. Been too busy cranking on a new product to stay up on the latest changes so thanks for the pointer. I have lots of reading to do.
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.
Lack of instance is intentional—otherwise we would’ve made it an instance method. Happy to hear about specific patterns you’re aiming for but since getDerivedStateFromProps executes during the “render phase” (interruptible in async mode) we want people to treat it as pure and discourage them from any side effects and mutations. If you really need some instance variable there then perhaps you could move it into state.
I have 27 componentWillReceiveProps across 286 components, so not a whole lot. But in almost none of those do I update the component state, the most common thing is to compare the previous and new props and dispatch some Redux action that will start a load of some data, which will eventually update the props (of multiple components) via Redux. I guess I should start using componentDidUpdate instead?
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.
> I guess I should start using componentDidUpdate instead?
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.
For different reasons I don't use Redux that much in my app. The state is persisted in the url, or to be more precise React-Router's location. So let's say I have five different components on the page. All of them receive location and history as props. Each component updates the url separately, let's say a table updates the url with sort1=blah now the component needs to read this param compare next and current and fetch the new data in componentWillReceiveProps. All components verify if the url was updated with the url param they need and only the one that had it changed will fetch data. I use componentWillReceiveProps everywhere. I'm not sure if there is another life cycle to do just that.
We intentionally made the naming a little bit obtuse. A name like `getStateFromProps` implies this is a common operation because it sounds so “normal”. And this is, in fact, a common misconception with React beginners who try to copy props into state instead of using props directly or lifting state up. So we wanted the naming to reflect that this pattern is not supposed to be the most common.
I was hoping they were going to simplify the lifecyles but it looks like they are adding more.
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.
Lifecycles aren't mandatory in React. Pure functional components don't use them. Stateful class components don't necessarily need them either (although they _can_ be useful, particularly when interfacing with imperative APIs like the DOM).
I've never used Hyperdom, so this isn't meant as a criticism or commentary on it.
In the unlikely scenario of Facebook's demise, how do you think the maintenance of React would transpire? Do you think some other tech company would snatch up the design leads, and pick up the torch? Would it go into its own independent organization?
I think developers will stop using ReactJS long before FB is gone. Look at angular, backbone, JQuery, Adobe Flex and so many others. It's just a framework....
Vue is great, I love it for my personal projects and it's also fairly common in PHP world but that's it. Angular is very popular in Java world and it works really great if you need a lot of forms. These people usually don't have time talking about it. React is popular everywhere else, especially among bloggers who usually fail to explain why exactly is React better choice.
I don't buy that. I actually see Angular being USED in big corporate environments. I hear from friends about how their team is USING it.
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.
I use and love React, but I'm also a little scared of it. I opened this little app I did 1 year ago in React 15, and it's completely incompatible with what I use now.
Every time I read of a new release I get this chill down my spine for a second.
Our upgrade process is very gradual. As long as you've fixed all of the dev warnings for the last minor release of a given version, you should be able to upgrade to the next major without any problem.
For someone who hasn't opened the app in three years going through the upgrading process for each major version/minor high version would be more difficult vs rewriting for most smaller components.
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.
Upgrading any project after 3 years of inactivity would be difficult, regardless of framework.
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.
The problem is when--like in my case--people develop a project for a client and then go on with their lives, until the client notices a bug, or wants to implement a new feature months after.
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.
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.
Maybe because it's an older and therefore feature-complete/mature project, or maybe because it's not being actively developed anymore (I really don't know), but jQuery's API has hardly changed in the past few years.
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.
I've been thinking about different situations in which Redux could be replaced with the new Context. Id sure appreciate having to write less boilerplate. The biggest concern I always get stuck on is where I'll put all my app's business logic, which I'm so used to putting in async redux-observable or redux-thunk actions. Does anyone have anything ideas on how to make an elegant business-logic-full Provider component?
I put up a post today called "Redux - Not Dead Yet!" [0], where I responded to recent comments claiming that the new context API will replace Redux. (Answer: maybe, if all you need is just passing down data to deeply nested components.)
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.
I have very mixed feelings about the new context api. On one hand, you're taking a foot-gun away, which I approve of having worked on code bases that abused it. On the other hand, it's very clunky, and I think people who abused the old context will just start making components larger, and wrapping them in a single context, rather than doing the right thing with many small components individually wrapped. Personally I think props inheritance, with IsolatedComponent as an escape hatch/boundary would have been much cleaner.
I think it's more intended to be utilized as an abstraction in something like Apollo. It'll be interesting to see what sort of cleaner patterns emerge out of using Contexts under the hood.
Well, I tend to make a lot of very small stateless components. If a component is 1-6 JSX tags, the new context API represents a boilerplate overhead of between 100 and ~17%. Additionally, the semantics of having a consumer's props.children be an immediately invoked function are a little bit unintuitive to me. I think having multiple ways to pass "props" increases the api surface area, having everything "just be props" would be simpler.
> Well, I tend to make a lot of very small stateless components. If a component is 1-6 JSX tags, the new context API represents a boilerplate overhead of between 100 and ~17%.
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. :)
If you're familiar with the scope inheritance behavior of angular 1.x, that would basically be a reasonable model (i.e. setProtoTypeOf on props objects down the chain). That would avoid people taking the shortcut of doing {...spreadProps} when they have to pass a lot of properties to many small child components, and let you distinguish locally passed props vs inherited props during debugging.
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).
Can someone point me to reasonably simple but not contrived examples that put createContext and forwardRef to good use? As I look at this from a Mithril perspective the value isn't obvious.
Context is a way to pass state down the component tree without it having to go through intermediate nodes.
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'm curious why the theme example I mentioned in my earlier comment- (and showed in the docs)- doesn't qualify as "useful" to you. Another example would be using context to share the selected language/locale between all of the localized components in an application.
In self-contained apps couldn't a single prop be passed down that's an object with all things like that in it? Maybe there's a scenario with 3rd-party components where context could make it look more elegant, but I don't have a clear picture.
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.
OK, it just boils down to me thinking one explicit prop for every component isn't too much boilerplate. I can see some teams liking the idea of shaving off that explicit prop as long as everybody knows about the implicit context(s) that also affect component behavior.
> s everybody knows about the implicit context(s) that also affect component behavior.
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.
In my experience, examples are only useful if they have two points of comparison side by side.
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
AFAIK the problem with the old context is that values in context were not used to determine whether or not the component tree should update. So if a context value in A changed, but no props/local state changed, it would not redraw the tree below it (B -> C). If C's render depended on the context value, it would not be redrawn to reflect the change.
No, but you could do it in a similar way as the "Dynamic Context" example [1] - store the authenticated user/status in component `state` and pass it down via a AuthenticationContext.Provider. Then anything that needs to know about it could use the AuthenticationContext.Consumer.
disclaimer: I don't like react. Outside of dom node diffing and a focus on components I don't think it brings much to the table considering the considerable complexity it brings.
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.
We maintain more than 50,000 components at Facebook, and I don’t believe your assessment that “APIs undergoes drastic changes every few months” is accurate. The component API has barely changed since the initial release five years ago.
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.
I apologize -- I should have written "API internals". I did not mean to state that the component API changed a lot, just seemingly that the internals did, with fiber and the like.
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.
> 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.
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. :)
This is a huge point that I do often miss when I think about react when used for react development...
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
>just seemingly that the internals did, with fiber and the like.
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.
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-....
Thanks for explaining so much about the process -- I didn't mean that the code was bad, just that there was an unexpected amount of growth.
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
For the most part, the changes really shouldn't affect typical users. The biggest change in React from a dev perspective was function components, maybe 2 years ago. If you keep your state outside of React, it's dead simple to use.
I think it's a great library and has only really gotten simpler and better. But, to each their own.
I agree that it's getting simpler and better -- tons of dedicated developers are making that happen. I wasn't trying to say that the rewrite shouldn't have happened or something, I just feel like there's a lot of zealotry festering, so many companies are picking React just to attract developers (anecdotal evidence: https://www.youtube.com/watch?v=u80GTmVtdG4&t=2898s).
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".
I looked up HOCs not long after writing the comment, and I agree -- this is much better than the mixin approach that used to be the way (though if I remember correctly, mixins were considered 'temporary' at the time).
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.
A HOC is nothing more than a factory in traditional OO programming. Just because you see an acronym you don't understand, doesn't mean the entire framework is flawed
I should have looked up HOC before I wrote that bit -- Higher Order Components do not seem like a particuarly bad design decision, but IIRC this pattern was introduced somewhere around the launch of React 16?
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.
Components are just functions -- HOC's are higher order functions. The pattern dropped out like all patterns drop out -- from use. Its nothing specific to React, but to functional programming
Just replied in the other spot -- I understand the similarities now, but I think this points to react still not being as simple as it could be -- if components were really as simple as being functions from state to UI, reacts API would be drastically smaller.
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.
> 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.
To be fair if a function gets called, any functions called from it also... get called (whether arguments to them have changed or not).
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).
It just seems like your misunderstandings are preventing you from understanding React properly, and that those misunderstandings could be rectified by more experience with the library. Learning is inherently difficult but sometimes its best to trust in the process and dive in :)
The downvotes are unfortunate, since this is a reasonable sentiment, here's an article I read a while ago that presented this point in a more nuanced fashion:
Ahh I thought they were the same -- admittedly I've never worked deeply with windows program (and do not plan to at any point in time), so I couldn't tell the difference
* 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.