preface

Vue 3.0 released its RC version just over a half month ago, and React released its RC version on the heels of that.

However, React17 doesn’t seem to offer much in the way of react16.x compared to Vue3’s huge improvement over VUe2.x.

The reactjs/reactjs.org documentation on GitHub even shows the following line:

No new features! This React is a bit of a jerk!

So what’s new? Let’s translate this document:

The translation

Address: github.com/reactjs/rea…

The title The author
React 17.0: No new features gaearon rachelnabors
Today, we released the first RC version of React V17. Since theThe last major version of ReactIt’s been two and a half years now, which is quite long by our standards! In this blog post, we’ll explain how the major release will affect you and how to try it out.
## No new features
React 17 is unusual in that it doesn’t add any new features for developers, instead focusing onUpgrade to simplify React itself.

We’re actively working on new features in React, but they don’t belong in this release. React 17 is a key part of our in-depth promotion strategy.

What makes this release special is that you can think of React 17 as a transition release that will make it safer to embed one React versioning tree into another.

escalating

React has been following an all-or-nothing upgrade strategy for the past seven years. You can stick with the old version, or you can upgrade the entire app to the new version. But there is nothing in between.

This approach has continued to this day, but we ran into the limitations of the all-or-nothing upgrade strategy. Many API changes, such as those made against the Legacy Context API, cannot be done in an automated manner. Most apps to date have probably never used them, but we still choose to support them in React. We had to choose between supporting the outdated API indefinitely or still using the old version of React for some apps. But neither plan is suitable.

So we want to offer an alternative.

React 17 now supports gradual upgrades to the React version. When you upgrade from React 15 to 16 (or React 16 to 17), you usually upgrade the entire app at once. This applies to most applications. However, if the code base was written years ago and is not well maintained, upgrading it becomes increasingly challenging. Although it is possible to use both versions of React on the page, React 17 still has problems with events.

We solved a lot of these problems with React 17. This will mean you’ll have more options when React 18 or a future version comes out. The preferred option is to upgrade the entire application at once, as before. But you can also choose to upgrade your app gradually. For example, you might migrate most of your applications to React 18, but leave some dialogs or child routes lazily loaded on React 17.

But that doesn’t mean you have to escalate. For most applications, upgrading all at once is still the best solution. Loading two React versions, even if one is loaded lazily on demand, is still not ideal. However, for larger applications that are not actively maintained, this option can be considered, and React 17 starts to keep these applications up to date.

We need to make some changes to React’s event system in order to achieve this escalation. These changes can affect the code, which is why React 17 is a major release. In fact, no more than 20 of the 100,000 + components are affected, so we expect most applications to be able to upgrade to React 17 without much impact. You can contact us if you have any problems.

An example of a step-by-step escalation

We have prepared a sample (GitHub) repository that shows how to lazily load older versions of React if necessary. This example is built using the Create React App, but a similar approach should work for other tools. Developers using other tools are welcome to write demos and submit PR.

Note: We have delayed other updates until after React 17. The goal of this release is to achieve a gradual upgrade. If upgrading React 17 is too difficult, our goal will be lost.

Change event delegate

Technically, it’s always possible to nest different versions of React within an application. However, it is difficult to implement because of how the React event system works.

In the React component, event handling is usually written inline:

<button onClick={handleClick}>
Copy the code

The DOM equivalent of this code is as follows:

myButton.addEventListener('click', handleClick);
Copy the code

But for most events, React does not attach them to the DOM node. Instead, React attaches a handler directly to the Document node for each event type, which is called an event delegate. In addition to its performance advantages over larger applications, it also makes it easier to add new features such as Replaying Events.

React has been automating event delegation since its release. When a DOM event is triggered on the Document, React will figure out which component to call, and React events will “bubble” up in the component. But in fact, native events have bubbled up to the “document” level, and React is an event handler installed in document.

But that’s the difficulty of escalating.

If there are multiple versions of React on a page, they will all register event handlers at the top level. This breaks e.topPropagation () if event bubbling is prevented in the nested tree structure, but the outer tree still receives it. This makes it very difficult to nest different versions of React. This concern is not unfounded – the Atom editor, for example, ran into the same problem four years ago.

This is why we changed the way we attach events at the bottom of React.

In React 17, React will no longer add event handlers to document. Instead, attach the event handler to the root DOM node of the render React tree:

const rootNode = document.getElementById('root');
ReactDOM.render(<App />, rootNode);
Copy the code

In the React 16 or earlier, the React will execute the document for most events. The addEventListener (). 17 will React in the underlying call rootNode. AddEventListener ().

Thanks to this change, it is now safer to nest old and new versions of the React tree. Please note that for it to work properly, both versions must be 17 or higher, which is the root reason why upgrading to React 17 is strongly recommended. In a sense, React 17 is a transition version that makes gradual upgrades possible.

This change also makes it easier for React to embed applications built using other technologies. For example, if the “shell” of your application is written in jQuery but newer code is written in React, e.topPropagation () in the React code will prevent it from affecting the jQuery code — as you would expect. In other words, if you no longer like React and want to rewrite your application (using jQuery, for example), you can convert React to jQuery from the outer layer without breaking the event bubble.

It has been verified that many of the issues reported on Issue Tracker over the years have been addressed by the new feature, most of which are related to integrating React with non-React code.

Note: You might wonder if this breaks Portals outside the root container. The answer is that React also listens to events on portals containers, so that’s not a problem.

Solve the hidden trouble

As with any major update, you may need to tweak your code. At Facebook, we adjusted about ten modules out of thousands to accommodate this update.

For example, if the module is used in the document. The addEventListener (…). With the DOM listener added manually, you might want to catch all React events. In React 16 or earlier, even if you call e.topPropagation () in the React event handler, the DOM listener you created will still fire because the native event is already at the document level. Using React 17 bubbling will be blocked (on demand), so your Document-level event listener will not trigger:

document.addEventListener('click'.function() {
  // If the React component calls e.topPropagation ()
  // Then the custom listener will not receive the click event
});
Copy the code

You can convert listening to using event capture to fix such code. To do this, you can use {capture: true} as the document. The third parameter of addEventListener:

document.addEventListener('click'.function() {
  // Now the event handler uses event capture,
  // So it can receive all click events!
}, { capture: true });
Copy the code

Note that this strategy is globally more adaptable. For example, it might fix existing errors in code that occur outside of the React event handler by calling e.topPropagation (). In other words, React 17’s event bubble is closer to the regular DOM.

Other major Changes

We kept major changes in React 17 to a minimum. For example, it does not remove task methods that were deprecated in previous versions. However, it does contain some other significant changes that, as a rule of thumb, are relatively safe. Overall, due to these factors, no more than 20 components out of 100,000 are affected.

Benchmarking browser

We have made some minor updates to the event system:

  • The onScroll event is no longer bubbled to prevent some confusion.
  • React’s onFocus and onBlur events have been switched to native FocusIn and FocusOut events at the bottom. They are closer to React’s existing behavior and sometimes provide additional information.
  • Capture events (for example, onClickCapture) now use the capture listener in the actual browser.

These changes will bring React closer to browser behavior and improve interoperability.

Note: Although focus events were switched from React 17 to Focusin, onFocus did not affect bubble behavior. The onFocus event always bubbles in React, and it continues to bubble in React 17 because it’s usually a more useful default. Look at the Sandbox to see the different checks that can be added for different specific use cases.

Removing an Event Pool

Remove “Event pooling” from React 17. It doesn’t improve the performance of modern browsers, and it confounds even experienced developers:

function handleChange(e) {
  setData(data= > ({
    ...data,
    // This crashes in React 16 and earlier:
    text: e.target.value
  }));
}
Copy the code

This is because React reuses event objects for different events in older browsers to improve performance and sets all event fields to NULL before them. In React 16 and earlier, users had to call e.pen ist() to properly use the event or properly read the required properties.

In React 17, this code executes as intended. The old event pool optimization operation has been removed so that the consumer can read the event field as needed.

This changed behavior, so we flagged it as a major update, but in practice we haven’t seen it affect Facebook (and even fix some bugs!). . Note that e.pen ist() is still available in React event objects, but it doesn’t have any effect.

Side effect cleanup Timing

We are trying to align the useEffect with the cleanup function.

useEffect(() = > {
  // This is the effect itself.
  return () = > {
    // This is its cleanup.
  };
});
Copy the code

Most side effects do not require a delayed view refresh, so React executes them asynchronously as soon as updates are reflected on the screen (in rare cases, you need a side effect to prevent redrawing. For example, if you need to get the size and location, use useLayoutEffect.

However, the side effect cleanup function (if any) runs synchronously in React16. We have found that this is not ideal for large applications because synchronization slows down view updates (for example, switching tabs).

In React 17, the side effect cleanup function is executed asynchronously — if a component is to be unloaded, the cleanup runs after the view has been updated.

This reflects how the side effects themselves work more closely. In rare cases where you may want to rely on synchronous execution, use useLayoutEffect instead.

Note: You may be wondering if this means that you will now be unable to fix warnings about setstates on unmounted components. Don’t worry, React handles this situation specifically and doesn’t issue setState warnings during the short interval between unload and cleanup. Therefore, the request or interval for canceling code can almost always be saved unchanged.

In addition, React 17 executes cleanup in the same order as the effect, depending on where they are in the tree. In the past, the order was sometimes different.

potential

Reusable libraries may need to be tested in depth for this situation, but we have only encountered a few components that have been interrupted by this problem. Such as:

useEffect(() = > {
  someRef.current.someSetupMethod();
  return () = > {
    someRef.current.someCleanupMethod();
  };
});
Copy the code

The problem is that someref. current is mutable, so it may have been set to null when the cleanup function was run. The solution is to store the changing value inside the side effect:

useEffect(() = > {
  const instance = someRef.current;
  instance.someSetupMethod();
  return () = > {
    instance.someCleanupMethod();
  };
});
Copy the code

We don’t want this to be an issue, but our version of the Eslint-plugin-React-hooks/Naked-deps Lint plugin (make sure you use it in your project) warns you about this.

Return consistent undefined error

In React 16 and earlier, returning undefined always returns an error:

function Button() {
  return; // Error: Nothing was returned from render
}
Copy the code

It’s easy to inadvertently return undefined:

function Button() {
  // We forgot to write ruturn, so this component returns undefined.
  // React will report an error instead of ignoring it.
  <button />;
}
Copy the code

Previously, React only did this on class and function components, but it did not check the return value of the forwardRef and memo components. This is due to a coding error.

In React 17, the forwardRef and Memo components behave the same as the regular function and class components. Error when returning undefined

let Button = forwardRef(() = > {
  // We forgot to write ruturn, so this component returns undefined.
  // React17 will report an error without ignoring it.
  <button />;
});

let Button = memo(() = > {
  // We forgot to write ruturn, so this component returns undefined.
  // React17 will report an error without ignoring it.
  <button />;
});
Copy the code

Return NULL if you do not want to render anything.

Native component stack

When you encounter an error in the browser, the browser will provide you with stack information with the name and location of the JavaScript function. However, the JavaScript stack is often not enough to diagnose problems, because the React tree hierarchy can be just as important. Not only do you want to know which Button is throwing the error, but you also want to know where the Button is in the React tree.

To solve this problem, React 16 starts printing the “stack of components” information when you encounter an error. Still, they are inferior to the native JavaScript stack. In particular, they’re not clickable in the console because React doesn’t know where functions are declared in the source code. Moreover, they are almost useless in production. Unlike a regular compressed JavaScript stack, which can be automatically restored to its original function location in the form of Sourcemap, with the React component stack, you must choose between the stack information and the bundle size in a production environment.

In React 17, a different mechanism was used to generate component stacks that stitched them together with the regular native JavaScript stack. This allows you to get fully symbolized React component stack information in a production environment.

React does this in a somewhat unconventional way. Currently, browsers do not provide a way to get the function stack frame (source file and location). Thus, when React catches an error, it reconstructs its component stack information through a temporary error thrown (and caught) from within the component above. This increases the performance penalty in the event of a crash, but only once per component type.

If you’re interested, you can read more about it in this PR, but for the most part, this mechanism won’t affect your code. What’s new from the consumer’s point of view is the ability to click on the component stack (because they rely on the native browser stack frame) and decode them in production just like regular JavaScript errors.

The big change is that for this to work properly, React will re-execute parts of some of the above functions and some class constructors on the stack after catching an error. Since rendering functions and class constructors should not have side effects (which is also important for SSR), this does not cause any practical problems.

Removing a private export

Finally, the major change worth noting is that we have removed some React internal components that were previously exposed to other projects. In particular, React Native for the Web used to rely on some internal component of the event system, but this dependency is fragile and often broken.

In React 17, these private exports have been removed. As far as we know, React Native for Web is the only project that uses them, and they have completed the migration to other methods that do not rely on those private export functions.

This means that older versions of React Native for Web won’t be compatible with React 17, but newer versions can use it. In fact, not much has changed since React Native for Web had to release a new version to accommodate its internal React changes.

In addition, we removed the ReactTestUtils. SimulateNative helper method. They were never recorded, did not do what their name suggested, and did not deal with the changes we made to the event system. If you want an easy way to trigger native browser events in your tests, use the React Testing Library instead.

The installation

We encourage you to try React 17.0RC as soon as possible and let us know if you have any problems with the migration process. Note that the release candidate is not as stable as the stable version, so do not deploy it into production.

To install React 17 RC using NPM, do the following:

NPM install [email protected] [email protected]Copy the code

To install React 17 RC using YARN, perform the following operations:

Yarn add [email protected] [email protected]Copy the code

We also provide a UMD build of React RC via CDN:

<script crossorigin src="https://unpkg.com/[email protected]/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/[email protected]/umd/react-dom.production.min.js"></script>
Copy the code

Refer to the documentation for detailed installation instructions.

Previous excellent article

  • Microsoft launches comments section on GitHub
  • Vue 3.0.3: New CSS variable passing and the latest Ref Proposal
  • You Yuxi: Ref Grammar Sugar Proposal
  • “Double 11 small black box is cool? Let’s use CSS variables to improve!”
  • “Don’t underestimate the nine grid, one question can let a candidate reveal his true colors!”
  • “Mobile Layout Interview Questions to test your CSS Skills (Center)”
  • A series of confusing behaviors after setting prototype Objects as Proxies
  • Vue’s Super Fun New Feature: DOM Portal
  • A fun new feature of Vue: Introducing JS variables into CSS
  • Create your own Visual Data Map without any libraries
  • “Use of React’s Super-popular CSS-in-JS Library in the Vue Project: Styled – Components”
  • Is It Finally Vue’s Turn to Inspire React?
  • A Small Pit in Vue3 on IOS
  • Upgrade your React project to Immer instead of Immutable by 2020
  • “Soul Interrogation from the Author of React Hooks and Immutable”
  • Good news, Vue3 official document is available in Chinese!
  • Hooks use of the New VUe-Router
  • Vue 3:20 Years of Status Updates
  • Yu Yuxi: The Design Process of Vue3
  • The Father of Node’s refactoring Deno is finally released. Will it Replace Node after all?
  • The Vue3 beta was released early this morning and openly supports scaffolding!