为什么跟踪状态历史记录的React组件无法直观呈现?

时间:2019-09-14 07:47:40

标签: reactjs

我有一个较大的更复杂的组件,其行为方式与我预期的不同,我将其缩小为一个更简单的组件:

import React, { useState, useRef } from "react";
import ReactDOM from "react-dom";

function App(props) {
  const [history, setHistory] = useState([props.arr]);
  const [currentHistoryIndex, setCurrentHistoryIndex] = useState(0);
  const itemsRef = useRef(null);

  function handleButtonClick() {
    //Step 1: Manipulate the DOM directly.
    //I want the user to do this but here I'm hardcoding functionality for convenience.
    itemsRef.current.insertBefore(
      itemsRef.current.children[2],
      itemsRef.current.children[1]
    );

    //Step 2: Read the DOM
    const newArr = [...itemsRef.current.children].map(e => e.innerText);

    //Rerender with new values
    setHistory([...history, newArr]);
    setCurrentHistoryIndex(currentHistoryIndex + 1);
  }

  //Click the button, and it shows that it should render the updated array. Yet visually, the DOM does not update.
  console.log(history, currentHistoryIndex);

  return (
    <div className="App">
      <div className="items" ref={itemsRef}>
        {history[currentHistoryIndex].map((item, i) => (
          <div key={i}>{item}</div>
        ))}
      </div>
      <button onClick={handleButtonClick}>Click</button>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(
  <App arr={["Cow", "Dog", "Fox", "Turtle", "Zebra"]} />,
  rootElement
);

这里是一个代码笔链接:https://codesandbox.io/s/keen-sea-4hitw

该组件的目的是显示值的数组(请参阅传递的arr属性)。但是,我希望能够更改此数组并跟踪这些数组的历史记录,以便在将来支持撤消/重做。

单击按钮时,我希望发生某种DOM操作。然后,我想使用Vanilla JS读取DOM,然后基于此读数来更新历史记录状态。现在,我希望该组件在视觉上重新呈现,但事实并非如此。但是,如果我再次单击该按钮,它将呈现。

令人惊奇的是,render方法中的console.logging显示数据可用;它只是没有渲染。

我知道通常您不会在响应中进行直接DOM操作,但是我有我的理由,而且我不明白为什么这样做会如此。有这种奇怪行为的解释吗?

1 个答案:

答案 0 :(得分:1)

这就是you shouldn't use index as a key的原因。 React使用密钥来确定元素是否已更改以及是否应重新呈现。这就是为什么键对于内容应该唯一。

遍历数组时,您将数组的索引分配为键,即使内容发生更改,键也始终保持不变。 React认为并没有什么改变,因为键与上一次渲染期间的键相同且顺​​序相同。

如果您使密钥对于要打印的内容唯一,它将开始起作用。

{history[currentHistoryIndex].map((item) => (
  <div key={item}>{item}</div>
))}