我有一个较大的更复杂的组件,其行为方式与我预期的不同,我将其缩小为一个更简单的组件:
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操作,但是我有我的理由,而且我不明白为什么这样做会如此。有这种奇怪行为的解释吗?
答案 0 :(得分:1)
这就是you shouldn't use index as a key的原因。 React使用密钥来确定元素是否已更改以及是否应重新呈现。这就是为什么键对于内容应该唯一。
遍历数组时,您将数组的索引分配为键,即使内容发生更改,键也始终保持不变。 React认为并没有什么改变,因为键与上一次渲染期间的键相同且顺序相同。
如果您使密钥对于要打印的内容唯一,它将开始起作用。
{history[currentHistoryIndex].map((item) => (
<div key={item}>{item}</div>
))}