React中的数据表:更改状态VS手动更新DOM; data- *属性VS匿名函数

时间:2018-06-08 19:47:26

标签: javascript reactjs

我有一个React组件,它根据从服务器获取的数据呈现单词表及其翻译。单词被分成组,同一个单词可以存在于多个组中。每个组在同一个表中都显示一个单独的<tbody>标记。

我有以下REST API路由来删除组中的单词:DELETE /api/groups/group_id/words/word_id。我想将此功能添加到我的React表中,以便每个表行都有一个删除按钮,单击该按钮时会向服务器发送一个AJAX请求,如果成功,则从表中删除该行。

从表中删除行的语义最正确的方法是更新保存数据的组件状态。像这样:

this.setState(state => {
  const groups = [...state.groups];
  const groupIndex = groups.findIndex(group => group._id == group_id);
  const group = groups[groupIndex] = { ...groups[groupIndex] }; // for the sake of immutability
  const words = group.words = [...group.words];
  const wordIndex = words.findIndex(word => word._id == word_id);
  words.splice(wordIndex, 1);
  return { groups };
});

这反过来会导致组件重新渲染。但问题出现了:重新渲染整个组件只是为了删除一个表行可能是一个过度杀手,因为this.state.groups并且必须再次遍历嵌套的单词数组(因为render()方法调用了数组&#39; map()方法,以便构建组件树),之后必须调用React的对帐。如果我们关心表现,这似乎是一个糟糕的主意。

我知道我应该在React中尽可能地避免它,但在这种特殊情况下,为了性能而转向好的旧DOM并不会更好吗?这就是我的意思:

deleteWord = (event, group_id, word_id) => {
  fetch(`/api/groups/${group_id}/words/${word_id}`, {
    method: 'DELETE'
  }).then(response => {
    if (!response.ok) throw new Error(response.statusText);
  }).then(() => {
    // event.target is the delete button that has been clicked
    const td = event.target.parentNode; // table cell
    const tr = td.parentNode; // table row
    tr.parentNode.removeChild(tr);
  });
};

我仍然可以更新状态以保持事物同步,但shouldComponentUpdate()返回false以避免不必要的计算。

所以我的第一个问题是我应该选择哪种方式?也许我错了,表现上的差异并不那么重要?

然而,这并不是我唯一的问题。我还没有说过如何在点击其中一个删除按钮时检索group_idword_id值。这正是我面临另一个困境的地方。

最明显的方法是在呈现时添加匿名函数作为事件侦听器:

const tables = this.state.groups.map(group => {
  const rows = group.words.map(({ _id, word, translations }) => (
    <tr key={_id}>
      <td>{word}</td>
      <td>{translations.join(', ')}</td>
      <td><a href="#" onClick={event => {
        this.deleteWord(event, group._id, _id)
      }}>Delete</a></td>
    </tr>
  ));

  return <tbody key={group._id}>{rows}</tbody>;
});

return <table>{tables}</table>;

但我认为如果需要重新渲染组件,我应该避免再次创建这些函数。因此,我一直在考虑的另一个选项是将HTML5 data- *属性添加到商店group_idword_id,然后通过event对象访问这些属性。我想知道它是否真的更好,这是我的第二个问题。

1 个答案:

答案 0 :(得分:0)

将对象,数组和函数传递给组件时,可以使用memoization,以防止它们在未更改时重新呈现。尽管整个表已经改变,但是各行没有改变,所以没有理由重新渲染每一行。如果将行与表组件分开和/或为它们提供足够的key,则重新呈现表不应导致行重新呈现。

您可以使用key来引用映射数组中的元素,以防止它们中的任何内容发生更改时重新呈现。当表格重新渲染时,它会看到带有键的行&#34; value1&#34;没有改变,因此不会重新渲染该行。它会看到带有键的行&#34; value2&#34;缺少,因此将它从DOM中删除。它会看到带有键&#34; value3&#34;的行。没有改变,因此不会重新渲染那一行。

在示例中使用匿名函数会导致在每次渲染时重新创建该函数。这会导致新指针作为prop传递给该元素,从而导致该元素重新呈现。您应该记住内联函数,对象和数组。