这是我的代码:
import "./styles.css";
import { useState } from "react";
export default function App() {
const [input, setInput] = useState("")
const [list, setList] = useState([
{key:584535, value:"Do laundry", isChecked: false},
{key:439735, value:"Take out trash", isChecked: false},
{key:589774, value:"Do dishes", isChecked: true},
{key:573735, value:"code", isChecked: false},
{key:589739, value:"margin", isChecked: false},
{key:585735, value:"textbox", isChecked: false}]
)
const [history, setHistory] = useState([[]])
function handleCheckClick(e) {
let checkkey = e.target.getAttribute("checkboxkey")
setHistory([...history, list])
list.map(
(element) => {
if(element.key == checkkey){
element.isChecked = !(element.isChecked)
}
}
)
setList([...list])
}
function handleRemoveClick(e) {
let buttonKey = e.target.getAttribute("buttonkey")
list.forEach(
(element, index) => {
if(element.key == buttonKey){
setHistory([...history, list])
setList(list.toSpliced(index, 1))
}
}
)
}
function handleSubmit() {
if(input == ""){return}
let key = Math.floor(100000 + Math.random() * 900000)
setHistory([...history, list])
setList([...list, {key: key, value: input, isChecked:false}])
setInput("")
}
function handleHistoryClick(){
if((history[history.length-1]) == undefined){return}
setList(history.pop())
setHistory([...history])
}
return (
<div className="App">
<h1>To Do</h1>
<input id="input" type="text" value={input} onChange={(e) => setInput(e.target.value)}></input>
<button onClick={handleSubmit}>Submit</button>
<button onClick={handleHistoryClick}>Undo</button>
<div id="container">
<ol>
{list.map(({key, value, isChecked}) =>
<>
{isChecked === true && <li id="strike" key={key}>{value}</li>}
{isChecked === false && <li key={key}>{value}</li>}
<input type="checkbox" checkboxkey={key} onClick={handleCheckClick}></input>
<button buttonkey={key} onClick={handleRemoveClick}>Remove</button>
<br />
</>)
}
</ol>
</div>
</div>
);
}
我有一个历史数组,它保存对主列表数组所做的所有更改的副本。当我尝试使用 list.map 中的
element.isChecked = !(element.isChecked)
将 isClicked 值分配给主数组时,它会更改所有历史数组中的值!这是每次使用“文本框”值切换复选框时 console.log 历史记录时的控制台图像,最后一次单击是将其设置为单击:
JavaScript 中的对象是引用。因此,当两个数组引用相同的对象时,改变一个数组中的对象将影响另一个数组中的对象。因为对于任何给定的实例,只有一个对象,它只是被多个数组/变量/等引用。
鉴于这是 React 代码,这里简单的教训是不要改变 React 中的状态。这就是这行代码的作用:
element.isChecked = !(element.isChecked)
(此外,这是对
.map()
函数的误用,逻辑上 .forEach()
或循环更合适。)
相反,在更新状态时将数组投影到新对象的新数组中。例如:
setHistory([...history, list]);
setList(list.map(
(element) => {
if(element.key == checkkey) {
return { ...element, isChecked = !element.isChecked };
} else {
return element;
}
}
));
这里的主要区别是
.map()
的回调返回的内容。如果“key”匹配,它将返回一个从现有对象加上一个修改后的属性构建的新对象。否则,它按原样返回对象。因此,任何需要更改的对象都会产生一个新对象,而不是改变现有对象。