簡體   English   中英

當使用.map索引作為鍵時,為什么React無法在JSX元素數組中呈現更改?

[英]Why React fails to render changes in array of JSX element when using .map index as a key?

在此代碼庫中,有React應用程序呈現ToDo的列表。 它使用.map() index參數作為key來呈現此列表。 是的-我知道這是這種行為的根源,所以請不要在回答中告訴這個問題。

初始加載后,我在此添加了一些項目,然后看到了:

在此處輸入圖片說明

然后我按日期對它進行排序,然后在第一項中輸入內容

在此處輸入圖片說明

然后單擊“ Sort by Latest並得到以下信息:

在此處輸入圖片說明

問題:在此特定示例中,當使用.map索引作為鍵時,為什么React無法在JSX元素數組中呈現更改?

PS這是為方便起見的代碼:

const ToDo = props => (
  <tr>
    <td>
      <label>{props.id}</label>
    </td>
    <td>
      <input />
    </td>
    <td>
      <label>{props.createdAt.toTimeString()}</label>
    </td>
  </tr>
);

class ToDoList extends React.Component {
  constructor() {
    super();
    const date = new Date();
    const todoCounter = 1;
    this.state = {
      todoCounter: todoCounter,
      list: [
        {
          id: todoCounter,
          createdAt: date,
        },
      ],
    };
  }

  sortByEarliest() {
    const sortedList = this.state.list.sort((a, b) => {
      return a.createdAt - b.createdAt;
    });
    this.setState({
      list: [...sortedList],
    });
  }

  sortByLatest() {
    const sortedList = this.state.list.sort((a, b) => {
      return b.createdAt - a.createdAt;
    });
    this.setState({
      list: [...sortedList],
    });
  }

  addToEnd() {
    const date = new Date();
    const nextId = this.state.todoCounter + 1;
    const newList = [
      ...this.state.list,
      {id: nextId, createdAt: date},
    ];
    this.setState({
      list: newList,
      todoCounter: nextId,
    });
  }

  addToStart() {
    const date = new Date();
    const nextId = this.state.todoCounter + 1;
    const newList = [
      {id: nextId, createdAt: date},
      ...this.state.list,
    ];
    this.setState({
      list: newList,
      todoCounter: nextId,
    });
  }

  render() {
    return (
      <div>
        <code>key=index</code>
        <br />
        <button onClick={this.addToStart.bind(this)}>
          Add New to Start
        </button>
        <button onClick={this.addToEnd.bind(this)}>
          Add New to End
        </button>
        <button onClick={this.sortByEarliest.bind(this)}>
          Sort by Earliest
        </button>
        <button onClick={this.sortByLatest.bind(this)}>
          Sort by Latest
        </button>
        <table>
          <tr>
            <th>ID</th>
            <th />
            <th>created at</th>
          </tr>
          {this.state.list.map((todo, index) => (
            <ToDo key={index} {...todo} />
          ))}
        </table>
      </div>
    );
  }
}

ReactDOM.render(
  <ToDoList />,
  document.getElementById('root')
);

似乎這里的輸入根本沒有更改,並且由於不受控制(因此它們的屬性/文本值沒有更改),因此在React對帳的上下文中根元素沒有更改(它仍然具有相同的key )。 然后,React轉到最終不同的文本節點,然后僅刷新(更新DOM)那些已更改的節點。

因此,有兩種變體可以使這項工作:

  1. 使用有意義的key ,而不是.map() index
  2. 在我們的特定情況下,將屬性添加到<input />

通常,第一種方法更好,因為key屬性會使元素(在我們的示例中為整個ToDo )無效,並強制其呈現自身,從而節省了對嵌套子樹的比較。

問題:在此特定示例中,當使用.map索引作為鍵時,為什么React無法在JSX元素數組中呈現更改?

不要將列表索引用作鍵。 關鍵是要識別您從一個渲染到另一個渲染的各個條目。 當您更改數組的順序時,react不會知道任何更改,因為todo數組中索引的順序仍然是0, 1, 2, 3 ...而是使用todo.id作為鍵。

React並未呈現新訂單。 完全符合預期。 您告訴它的順序與上次渲染完全相同,但是各個待辦事項的屬性已更改。

您要說的是順序已更改。 然后,您必須傳遞一個有意義的密鑰。 然后react將使用相同的dom元素,但是會更改順序。

這意味着您可以執行諸如react-shuffle之類的操作 ,在該過渡效果中,您可以查看元素的重新排序方式。

在此處輸入圖片說明

這是因為您沒有穩定的ID(您的索引在過濾后會更改)。 此處查看文檔。

還有另一個問題,因為對列表進行排序時會對其進行突變:

sortByEarliest() {
    const sortedList = this.state.list.sort((a, b) => {
      return a.createdAt - b.createdAt;
    });
    this.setState({
      list: [...sortedList],
    });
  }

  sortByLatest() {
    const sortedList = this.state.list.sort((a, b) => {
      return b.createdAt - a.createdAt;
    });
    this.setState({
      list: [...sortedList],
    });
  }

排序會改變您的數組。 因此,您可以使用以下解決方案:

  sortByEarliest() {
    const { list } = this.state;
    list.sort((a, b) => a.createdAt - b.createdAt);
    this.setState({ list });
  }

  sortByLatest() {
    const { list } = this.state;
    list.sort((a, b) => b.createdAt - a.createdAt);
    this.setState({ list });
  }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM