All of a sudden, there was a feedback from an overseas user, who said that a page would go blank if you click the “add” button. The other party could not speak Chinese, so the whole process of communication was in English, using the level 6 dumb English that I picked my feet. The communication process was a little more difficult. At the beginning of the white screen, there is no fluctuation in the heart, this kind of problem is nothing more than a certain interface data return is not scientific, and then the front end has no fault tolerance. Only need to see the error message can be resolved in seconds

The front row tips, nuggets now when the release of banned words will not be sent out. So I spent half an hour sending an article, repeatedly using dichotomy to exclude the location of prohibited words, can be sent to explain that the previous content is no problem, and then add a little content to continue to try. When I was still sending the article, I saw 200 browsed and apologized to 200 small friends. At that time, I was still trying sensitive words, the content of the article was not complete. Now it is good and I can look back. For example, “error” sometimes needs to be changed to “error”, pages cannot have emojis, and “jiechi” is prohibited (hijack later). It is highly recommended to give a list of banned words, or pop up a reminder when you detect a banned word

Let the user open the console

Let the user refresh and repeat, keeping the console state open. How to open the console to which panel, and then ask the other side to take a screenshot, the result is this error:

Cannot read property xx of undefined React source code setstate after dispatch, trigger batch update, execute scheduling error. React was suddenly overwhelmed when some other operation changed the DOM node. Even if I knew that, how would I know? Then directly to retrieve the interface data, put the local run to see if you can reproduce it

Instruct the user to send a response

After a bit of foot English communication and step screenshots, finally let the user send the relevant interface return data. I got the numbers. It’s my turn to perform. I start running dev locally and delegate all these interfaces to the data I just got

As a result, it worked perfectly and nothing happened

Then I try to see the other side of the screen recording, it turns out that there is no error operation, only click the button, and the react source code is the same error, the interface is normal. Finally, I decided to let the user scan my computer code and log in to my computer account

In my computer boarded the number of others, began a meal operation, came to the same page, click the button, the result is normal, what did not happen…… Do you have a lot of question marks

Remote desktop

Out of options, I video-called and asked for screen sharing. I got through and started the whole process of oral communication. I could only speak my spoken English slowly, and I guess the other party could barely understand it. I repeated the previous operation, sure enough, there again, came to the same page, click the button, immediately reported an error. It’s the same problem

So began to interrupt the point, the operation of a few, incredibly their own good! ??

Behind refresh the page, all natural good……..

Heart tired, temporarily no matter so much, nothing is good, things so far.

“looks fine for now. Thank you so much!”

Things come up again

A few days later, while happily writing requirements, I was suddenly pulled by the robot. It was the same person, the same question, but a different page link. Before you start, let’s get this straight:

  • React source code error, there must be a native DOM operation outside react
  • The code has been verified that there are no other native DOM operations
  • They’re doing dom manipulation on the console, right? No way. No technical background
  • It has to be browser plugins, man-in-the-middle injection (almost impossible with lowest priority), translations
  • Forget about the last interruption. No cutting corners

Last time I learned that direct remote control is the best approach. And immediately connected to the remote control. Checked the browser plugins, nothing affected – browser plugin pass. Confirm whether the translation, asked the other party said whether open the translation, the other party said no (remote desktop can not see the pop-up menu, so I need someone to tell me)

Ok, they say there is no translator, so I assume that is true. Since the root cause of the problem is native DOM operations outside react, the dom node count is likely to be different. So I typed it on the console, okay? (‘*’) to find 2400 nodes on the other computer. If I type it on my computer, there are only 2000 nodes. Ask a colleague to help see, also 2000 nodes. So I decided to compare the first different node and typed a simple script on their computer console:

? (The '*').reduce((acc, { tagName }) = > `${acc}${tagName}, `.' ')
Copy the code

I: “Could you please copy the TXT and send me”

So I got all of the tag strings from the user’s entire page, and compared them to mine in the console of the page I opened:

var arr = otherHtml.split(', ')
?(The '*').findIndex(({ tagName }, i) = >tagName ! == arr[i])Copy the code

It was found that the index was 103 and the 103rd node was found to be a link tag which introduced a CSS at translate.googleapis.com and the HTML tag added a class called translated- LTR. As the name suggests, the translation is solid

Then, continue to expand the main content, found that the other side of the page many font tags!!

Sure enough, or open the translator, but people “feel not open”. In fact, it is likely that the previous set of uniform translation, so the back has been no matter, all websites will automatically translate. I then asked the user to turn off the translation as I asked. In the end, repeated operation, the problem did not appear

In fact, it is estimated that before everyone is scaffolding a brush, and did not notice the HTML lang value, and our system is English. So there is a “Chinese” page with all the content in English. The logic of overseas Chrome translation is that this is a “Chinese” page, which needs to be automatically translated, and then “English translated into English”. There is no visual change, in fact, dom nodes have a lot of font

<html lang="zh-cn">
Copy the code

Why was it okay the last time

So I wanted to see why the break point didn’t work last time, and open Wikipedia to see what happens to the break point with translation enabled. Open the Source panel and check the Load event

Automatic translation is also enabled

<html class="client-js" lang="en" dir="ltr">
Copy the code

When I clicked the next two steps, the HTML tags changed. The core features were: translated LTR

<html class="client-js translated-ltr ve-not-available" lang="zh-CN" dir="ltr">
Copy the code

Look again at the Element panel, which has a lot of font wrapping

  • I have a breakpoint on the return of the last interface before the error occurs, and check the breakpoint of the error event
  • A cORS error is reported and error is stuck. At this time, there is already a request out, and the break button buys time (you seem to be pending, but actually the response has already arrived at your door).
  • Click the next step, the data in front of the second out, the moment again stuck, because the last interface also came back
  • This is not the time to pull the translation resources, but the page is displayed completely. With a click of a button, I managed to get past the translation confusion. This is a create button, after the successful creation is the user’s own operation
  • Because creation is a slightly less frequent activity, no feedback is received for several days
  • The problem is usually when you delete an element after setState, that element can’t be traced back to an error. Clicking the button here does delete the button and switch the page

React: How do I react

React: How do you react


const { useState, useLayoutEffect } = React;
export default function App() {
  useLayoutEffect((a)= > {
    const font = document.createElement("font");
    const app = document.querySelector(".App");
    // Create the effect of font wrap, simulate the effect of translation, destroy the original structure
    while (app.firstChild) {
      font.appendChild(app.firstChild);
    }
    app.appendChild(font);
    setTimeout((a)= > {
    // set state
      setShow(false);
    }, 1000); } []);const [show, setShow] = useState(true);
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      {show && (
        <>
          123123
          <h2>Start editing to see some magic happen!</h2>
        </>
      )}
    </div>
  );
}

Copy the code

The desired effect occurred:

In fact, there is no need to manually change, you just need to right click on the translation for Chinese can reproduce. React stores parentNode in advance, so child nodes cannot be found

The solution

Error boundary component

React’s two life cycles are used to sense translation errors and then display a bottom-pocket UI that prompts the user to turn off the translation. The operation document link is also given. When using it, just wrap the component with TranslateErrorBoundary

class TranslateErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { translateError: false };
  }

  static getDerivedStateFromError() {
    if (document.documentElement.classList.contains("translated-ltr")) {
      return { translateError: true };
    }
  }

  componentDidCatch(e, info) {
  // A translation error is reported
    report(e, info);
  }

  render() {
    if (this.state.translateError) {
      return (
        <>
          <strong>
            translate error! you' d better to turn your google-translate off and reload. see
          </strong>
          <a
            target="_blank"
            rel="noopener noreferrer"
            href="Document link"
          >The document</a>
        </>
      );
    }
    return this.props.children;
  }
}

// usage
<TranslateErrorBoundary>
    <Cpn />
</TranslateErrorBoundary>
Copy the code

Do not have a text node in the outermost layer of a react element that can be deleted

Without further ado, see 🌰

<div className="App">
  <h1>Hello CodeSandbox</h1>
  {show && (
    <>
      123123
      <h2>Start editing to see some magic happen!</h2>
    </>
  )}
</div>

Copy the code

This section has the outermost text node 123123, so the translation will error:

{show && (
  <>
    123123
    <h2>Start editing to see some magic happen!</h2>
  </>
)}
Copy the code

Why is that? Delete node “123123”, but its parent node can not find it any more

{show && (
  <>
    <font><font>123123</font></font>
    <h2><font><font>Start editing to see some magic happen!</font></font></h2>
  </>
)}

Copy the code

Corrective action: Add span tags and don’t make 123123 bare

{show && (
  <>
    <span>123123</span>
    <h2>Start editing to see some magic happen!</h2>
  </>} // after translation {show && (<>
    <span><font><font>123123</font></font></span>
    <h2><font><font>Start editing to see some magic happen!</font></font></h2>
  </>)}Copy the code

Because the outermost element is a span, if you add a font, you add it inside a span. If you delete the element, you will find a span

Look at another 🌰

<div> {label ! == undefined ? ( <div> {label} </div> ) : null} {children} </div>Copy the code

In this case, label is plain text. After the example above, we know that {label} has a span. But there is a risk: if this component is used externally, and the externally is passed in by children, meaning that the content of children is variable, such as passing in a string, and setstate is followed by another node, then the problem arises again

The error condition is repeated: a block of the react element that can be deleted has text nodes in its outermost layer. At this point, children is a piece of element, and it is variable. The outermost node is the outermost node of the object children, and there is a text node that is a string, so the error condition is met

TextNode1 children is text node, for example, so normally after setstate if children changes, delete textNode1 way textNode1ParentNode. RemoveChild (textNode1). TextNode1 is no longer a child of textNode1ParentNode if it is translated and the text node wraps two layers of font. In addition, even if you change the outer div to span, section, or article, you will get an error

Corollary: Do not expose mutable text nodes directly under any element

All the code is written by myself, such as props. Children, such as props. Children, such as props. You can actually write a tool that scans the AST and automatically wraps a span with bare text nodes

Or, make a issue ask react there can not save to the parent node, delete elements directly when node. ParentNode. RemoveChild?

conclusion

  • Frameworks that use data-driven views like React and Vue, if you encounter a source error, consider whether native DOM operations are being scrambled
  • If you’re sure it’s not native DOM manipulation, consider browser plugins and translations
  • You do need to use native operations in React and Vue. You need to take this risk into account
  • If this problem occurs in international services, it is recommended to start troubleshooting from browser translation
  • Don’t let a react element that can be deleted have text nodes at its outermost layer. Make sure there are mutable text nodes

Pure write demand write business boring? Let’s get together. Pay attention to the public account “Different front-end”, learn front-end from a different perspective, grow fast, play with the latest technology, explore all kinds of black technology