繁体   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