[Translation] React 17 finally released the RC version, the official said that 17 is a transitional version!

Preface

Vue 3.0 just released the rc version half a month ago, and React released the rc version shortly thereafter.

However, compared to Vue3's huge improvement in Vue2.x's capabilities, React17 doesn't seem to have any very powerful updates to React16.x.

There is even this sentence in the reactjs/reactjs.org document on GitHub :

There are no new features! React is a bit skinny this time!

So what has it been updated? Let's translate this document and have a look:

Translation

Document address: https://github.com/reactjs/reactjs.org/blob/c30ff1e39b9fca747198c028a33300656a90e612/content/blog/2020-08-10-react-v17-rc.md

title Author
React 17.0: no new features gaearon rachelnabors

Today, we released the first RC version of React v17. It has been two and a half years since the last major version of React . According to our standards, the time span is a bit longer! In this blog, we will explain the impact of this major version on you and how to try it.

No new features

React 17 is unusual because it doesn't add any new features for developers, but focuses on upgrading and simplifying React itself .

We are actively developing new features in React, but they are not part of this version. React 17 is the key to our in-depth promotion strategy.

The reason why this version is special is that you can think of React 17 as a transitional version , which will make it safer to embed a React version management tree into another React version management tree.

Upgrade gradually

In the past seven years, React has followed an all-or-nothing upgrade strategy. You can continue to use the old version or upgrade the entire application to the new version. But there is no situation in between.

This method has continued to this day, but we did encounter the limitations of the all-or-nothing upgrade strategy. Many API changes, for example, when opposing the use of the legacy context API , cannot be done in an automated way. It is possible that most applications have never used them so far, but we still choose to support them in React. We have to choose between supporting outdated APIs indefinitely or still using the old version of React for some applications. But these two schemes are not suitable.

Therefore, we want to provide another solution.

React 17 began to support the gradual upgrade of the React version . When it goes from React 15 to 16 ( or from React 16 to 17 ), it usually upgrades the entire application at once. This applies to most applications. However, if the code base was written a few years ago and is not well maintained, then upgrading it will become more and more challenging. Although two versions of React can be used on the page, there will still be issues with events until React 17.

We use React 17 to solve many such problems. This will mean that when React 18 or a future version comes out, you will have more choices . The first choice is to upgrade the entire application at once as before. But you can also choose to upgrade your application gradually. For example, you might migrate most of your applications to React 18, but keep some delayed loading dialogs or sub-routes on React 17.

But this does not mean you have to upgrade gradually. For most applications, upgrading all at once is still the best solution. Loading two React versions, even if one of them is lazily loaded on demand, is still not ideal. However, for large applications that are not actively maintained, this solution can be considered, and React 17 can guarantee that these applications are not out of date.

In order to achieve a gradual upgrade, we need to make some changes to React's event system. These changes may have an impact on the code, which is why React 17 has become a major version. In fact, there are no more than 20 affected components out of more than 100,000 components, so we hope that most applications can be upgraded to React 17 without too much impact . If you encounter problems, you can contact us .

Example of gradual upgrade

We have prepared a sample ( GitHub ) repository that shows how to delay loading the old version of React when necessary. This example uses Create React App to build, but a similar approach to other tools should also apply. We welcome developers who use other tools to write demos and submit pr.

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

Change event delegate

Technically, it is always possible to nest different versions of React in an application. But due to the working principle of the React event system, it is difficult to implement.

In React components, event handling is usually written inline:

<button onClick={handleClick}>

The DOM operation equivalent to this code is as follows:

myButton.addEventListener('click', handleClick);

But for most events, React does not attach them to the DOM node. On the contrary, React will directly attach a handler for each event type on the document node, which is called event delegation . In addition to its performance advantages on large applications, it also makes it easier to add new features like replaying events .

Since its release, React has been automating event delegation. When a DOM event is triggered on the document, React will find out which component is called, and then 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 the document.

But this is where the difficulty of escalation lies.

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

This is why we want to change the way React attaches events at the bottom.

In React 17, React will no longer add event handlers to the document. Instead, the event handler will be attached to the root DOM node that renders the React tree:

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

In React 16 or earlier, React will execute document.addEventListener() for most events. React 17 will call rootNode.addEventListener() at the bottom.

Thanks to this change, it is now possible to nest old and new versions of React trees more safely . Please note that for it to work, 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 transitional 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 the application is written in jQuery, but the newer code is written in React, the e.stopPropagation() in the React code will prevent it from affecting the jQuery code - just like you As expected. In other words, if you no longer like React and want to rewrite your application (for example, with jQuery), you can convert React to jQuery from the outside without breaking event bubbling.

After verification, many of the issues reported on the issue tracker over the years have been resolved by new features. Most of these issues are related to the integration of React with non-React code.

Note: You may be wondering if this will break Portals outside of the root container . The answer is that React also listens to events on the portals container, so this is not a problem.

Solve hidden dangers

As with other major updates, the code may need to be adjusted. At Facebook, we adjusted approximately ten modules among thousands of modules to accommodate this update.

For example, if you manually add DOM listeners in the module using document.addEventListener(...), you may want to be able to capture all React events. In React 16 or earlier, even if you call e.stopPropagation() in the React event handler, the DOM listener you created will still trigger 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() {
    
    
  // 如果React组件调用了e.stopPropagation()
  // 那么这个自定义监听函数不会收到click事件
});

You can convert monitoring to use event capture to fix this type of code. To do this, you can pass {capture: true} as the third parameter of document.addEventListener:

document.addEventListener('click', function() {
    
    
  // 现在这个事件处理函数使用了事件捕获,
  // 所以它可以接收到所有的点击事件!
}, {
    
     capture: true });

Please note that this strategy is more adaptable globally. For example, it may fix existing errors in the code that occur when e.stopPropagation() is called outside of the React event handler. In other words, React 17's event bubbling is closer to regular DOM .

Other major changes

We keep major changes in React 17 to a minimum. For example, it will not remove task methods that were deprecated in previous versions. However, it does contain some other major changes, and based on experience, these changes will be relatively safe. In general, due to the existence of these factors, no more than 20 components are affected among more than 100,000 components.

Benchmarking browser

We have made some minor updates to the event system:

  • The onScroll event is no longer bubbling 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.
  • The capture event (for example, onClickCapture) now uses the capture listener in the actual browser.

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

Note: Although the focus event was switched to focusin from React 17, onFocus did not affect the bubbling behavior. In React, the onFocus event is always bubbling, and it continues to bubbling in React 17, because it is usually a more useful default. Check out this sandbox to learn about the different checks that can be added for different specific use cases.

Remove event pool

"Event pooling" was removed in React 17. It doesn't improve the performance of modern browsers, and even confuses experienced developers:

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

This is because React reused event objects for different events in old browsers to improve performance and set all event fields to null before them. In React 16 and earlier versions, users must call e.persist() to use the event correctly, or read the required attributes correctly.

In React 17, this code can execute as expected. The old event pool optimization operation has been deleted, so users can read event fields when needed.

This changed the behavior, so we marked it as a major update, but in practice we did not see it having an impact on Facebook (it even fixed some bugs!). Please note that e.persist() is still available in the React event object, but it has no effect.

When to clean up side effects

We are aligning the timing of useEffect with the cleanup function.

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

Most effects do not need to delay refreshing the view, so React executes them asynchronously immediately after the update is 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 position , Please use useLayoutEffect).

However, the side-effect cleanup function (if it exists) runs synchronously in React16. We found that this is not ideal for large applications, because synchronization will slow down the update of the view (for example, switching tabs).

In React 17, side-effect cleanup functions are executed asynchronously-if you want to uninstall the component, cleanup will run after the view is updated.

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

Note: You may be wondering if this means that you will now be unable to fix the warning about setState on unmounted components. Don't worry, React specifically handles this situation and will not issue a setState warning during the short interval between uninstall and cleanup. Therefore, the request or interval to cancel the code can almost always be preserved.

In addition, React 17 will execute the cleanup in the same order as the effect according to their position in the tree. In the past, the order was sometimes different.

Potential hazards

Reusable libraries may require in-depth testing of this situation, but we only encountered a few components that would interrupt execution due to this problem. such as:

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

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

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

We don't want this problem to affect everyone. The lint plugin of eslint-plugin-react-hooks/exhaustive-deps provided by us (please make sure to use it in the project) will warn you about this situation.

Returns a consistent undefined error

In React 16 and earlier versions, returning undefined will always report an error:

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

It's easy to return undefined unintentionally:

function Button() {
    
    
  // 这里忘记了写ruturn,所以这个组件返回了一个undefined。
  // React会报错而不会忽略它。
  <button />;
}

Previously, React only performed this operation on class and function components, but did not check the return values ​​of forwardRef and memo components. This is due to coding errors.

In React 17, the behavior of forwardRef and memo components will be consistent with regular function components and class components. An error will be reported when undefined is returned

let Button = forwardRef(() => {
    
    
  // 这里忘记了写ruturn,所以这个组件返回了一个undefined。
  // React17会报错而不会忽略它。
  <button />;
});

let Button = memo(() => {
    
    
  // 这里忘记了写ruturn,所以这个组件返回了一个undefined。
  // React17会报错而不会忽略它。
  <button />;
});

If you don't want to perform any rendering, please return null.

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 usually not enough to diagnose the problem, because the hierarchy of the React tree may be equally important. Not only do you need to know which Button threw the error, but you also want to know where the Button is in the React tree.

In order to solve this problem, when you encounter an error, starting from React 16, the "component stack" information will be printed. Nevertheless, they are still not as good as the native JavaScript stack. In particular, they are not clickable in the console, because React does not know where the function is declared in the source code. In addition, they are almost useless in production . Different from the conventional compressed JavaScript stack, they can be automatically restored to the original function position in the form of sourcemap. With the React component stack, you must choose between stack information and bundle size in a production environment.

In React 17, a different mechanism is used to generate the component stack, which stitches them together with the regular native JavaScript stack. This allows you to obtain fully symbolized React component stack information in a production environment.

The way React achieves this is a bit unconventional. Currently, the browser cannot provide a method to obtain the function stack frame (source file and location). Therefore, when React catches an error, it will reconstruct its component stack information through the temporary error (and capture) thrown inside the component. This will increase the performance loss at the time of the crash, but it will only happen once per component type.

If you are interested, you can read more details in this PR , but in most cases, this mechanism will not affect your code. From the user's point of view, the new feature is that you can click on the component stack (because they rely on the native browser stack frame), and can be decoded in production like regular JavaScript errors.

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

Remove private export

Finally, the notable major change is that we removed some internal components of React that were previously exposed to other projects. In particular, React Native for Web used to rely on certain internal components 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 the old version of React Native for Web will not be compatible with React 17, but the new version can use it. In fact, there is not much change, because React Native for Web must release a new version to adapt to the changes in its internal React.

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

installation

We encourage you to try the React 17.0 RC version as soon as possible, and you can ask us if you encounter any problems during the migration process . Please note that the candidate version is not stable, so please do not deploy it to a production environment.

To install React 17 RC version via npm, please execute:

npm install [email protected] [email protected]

To install React 17 RC version through yarn, please execute:

yarn add [email protected] [email protected]

We also provide a UMD build version 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>

For detailed installation instructions, please refer to the documentation .

Guess you like

Origin blog.csdn.net/GetIdea/article/details/107946774