React JSX React projects almost always use JSX, but JSX cannot be implemented directly. It needs to be compiled into JS code by Babel.

<div className="content">Hello world! </div>Copy the code
Through the Babel, preset

It is compiled into the following code and then executed

React.createElement("div", {
    className: "content"
}, "Hello world!");
Copy the code

A VNode can be created by calling the static method React.createElement.

According to the React documentation, this method can accept more than three arguments:

The first argument is type, which specifies the node type, or if it’s an HTML native node, it’s a string, like “div”; If it’s a React component, it’s a class or function. The second argument is props, which is an object or NULL. For example, in the previous example, the “className” attribute from the div tag is added here; Third (fourth, fifth…) ChildNode, the childNode of this node. In the previous example, the child node of a div is a “Hello world!” The TextNode

Yeah, DOM diff concrete diff stuff, those are the parameters. Why wouldn’t there be something else? React was designed to be a one-way mapping of Data => UI.

DIFF problem for dynamic lists

We know that React will compare the old and new Virtual DOM when invoking setState to trigger render, and strive to complete the new DOM rendering task at the lowest cost.

Combined with the above mentioned parameters, the specific comparison process is roughly as follows:

1 First compare type. If the type is different, then there is nothing to say. If the type is the same, look at the following: 2 second, compare the props, if there is any change, then update the changed part; Again, update if there is a change and do nothing if there is no change. This works fine in the normal case where the DOM structure is fixed, but it gets a little tricky when we want to map a list from a list whose items can change at any time.

For example, the original list would look like this:

[{name: 'Smith'.job: 'Engineer'},
  {name: 'Alice'.job: 'HR'},
  {name: 'Jenny'.job: 'Designer'}]Copy the code

Jenny was moved to the front, Smith and Alice were moved to the front

[{name: 'Jenny'.job: 'Designer'},
  {name: 'Smith'.job: 'Engineer'},
  {name: 'Alice'.job: 'HR'}]Copy the code

React: If it does not know who the three nodes are, it will find that each node is changed:

Smith => Jenny Alice => Smith Jenny => Alice React: All nodes in the list need to be updated and re-rendered.

Wait a minute! Is there a better way? ??

3 Use Key to solve the problem

React would be much simpler if it knew who these three nodes were:

There is no need to update any DOM nodes, just remove Jenny’s corresponding node and insert it into the new location.

But how would React know who was who?

This required us developers to tell it manually, and key was introduced.

When DOM diff is performed, if two VNodes under the same parent component have the same key, they are treated as the same node. If React determines that the node is in a different position in the list, it will perform “pick and insert” as described above.

To prove it, light up the code! Let’s start with a deliberately buggy version:

class App extends React.Component {
  state = {
    list: [0.1.2]}add() {
    const list = this.state.list;
    this.setState({ list: [list.length, ...list] });
  }

  render() {
    return (
      <div className="App">
        <button onClick={()= > this.add()}>Input sth below, then click me</button>
        <ul>This.state.list. map((item, index) => (<li key={index}>
                  <span>Item-{item}</span>
                  <input type="text" />
                </li>))}</ul>      
      </div>
    );
  }
}ReactDOM.render(  <App />.document.getElementById('root'));
Copy the code

Start a project with create-react-app and try this code locally. To demonstrate this, enter some ones in the second text box:

Then, click on the button above and you’ll find…

The textbox with a string of ones does not follow Item-1, but stays “in place”!

This is a classic bug caused by using array subscripts as keys. The reason is that item-0 in the new list has the same key as item-1 in the original list. React treats item-1 as the same node, so it only updates the child node (text) “in place” without moving the node’s position.

The trick of this bug is to use , which can be changed by the user’s behavior (input content) on the premise that the type, props, and children of VNode are not changed, so that we can intuitively see the position of node. Thanks to React officials for this clever case.

Ok, let’s fix this bug.

The fix is simple: change key={index} to key={item}.

Save, refresh and retry, we get:

React correctly identifies the three old nodes and inserts the new nodes directly at the top of the list, leaving the old nodes unchanged.

End of the paper. Now you can see what key is for and why index should not be a key.