由于 useRef,React useState 没有更新
React useState not updating because of useRef
我的 React 代码遇到了一个非常奇怪的问题:useState 没有更新视图,在尝试了一切之后问题仍然存在。我做了一个简单的代码来解释这个问题:
function(){
const [enterJob, setEnterJob] = useState(false);
const [jobSelection, setJobSelection] = useState(Array(someList.length).fill(false));
const jobRef = useRef();
const handleJobClick = i => {
const n = parseInt(i.target.id.charAt(0)); // the list is small enough to allow this
let c = jobSelection;
c[n] = !c[n];
setJobSelection(c);
};
const handleMouse = (e) =>{
if (!jobRef.current.contains(e.target)){
setEnterJob(false);
};
};
useEffect(() => {
window.addEventListener("mousedown", handleMouse);
return () => window.removeEventListener("mousedown", handleMouse);
});
return(
<div ref={jobRef}>
<input onFocus={()=> setEnterJob(true)} />
<div style={{display: `${enterJob ? 'flex' : 'none'}`}} >
<ul>
{ someList.map((item,index)=>
<li id={`${index}`} onClick={handleJobClick}> {jobSelection[index] ? item : "you clicked on the button"} </li> )}
</ul>
</div>
</div>
)
}
一些解释:我正在使用 UseEffect 和 useRef 创建一个下拉菜单,当您在容器外单击时该菜单会消失。现在,当我想点击此下拉菜单的值时,它不会更新 DOM,而我正在使用 useState 来更新负责更改的字符串的值。
提前谢谢你,
夏贝尔
问题是您正在改变 jobSelection
而不是创建新对象。如果对象具有与以前相同的引用,则 React 将跳过重新渲染:
const handleJobClick = i => {
const n = parseInt(i.target.id.charAt(0)); // the list is small enough to allow this
let c = [...jobSelection]; // Create a new array
c[n] = !c[n];
setJobSelection(c);
};
问题
如果我理解你的问题,那么我相信这是因为你直接改变了你的状态。
const handleJobClick = i => {
const n = parseInt(i.target.id.charAt(0)); // the list is small enough to allow this
let c = jobSelection;
c[n] = !c[n]; // <-- mutation!
setJobSelection(c);
};
您还缺少映射列表项上的反应键。
解决方案
由于下一个状态取决于前一个状态,您应该先使用功能状态更新来复制您的状态,然后更新它。
我建议:
- 将
handleJobClick
转换为直接使用索引,柯里化函数可以干净地处理这个问题
- 向映射的列表项添加反应键
代码
const handleJobClick = index => () => {
setJobSelection(jobSelection => jobSelection.map(
(selection, i) => index === i ? !selection : selection // <-- toggle selection at matched index
);
};
...
<ul>
{someList.map((item, index)=> (
<li
key={index} // <-- index as react key, ok since not adding/removing/sorting jobs
onClick={handleJobClick(index)} // <-- pass index to handler
>
{jobSelection[index] ? item : "you clicked on the button"}
</li>
))}
</ul>
我的 React 代码遇到了一个非常奇怪的问题:useState 没有更新视图,在尝试了一切之后问题仍然存在。我做了一个简单的代码来解释这个问题:
function(){
const [enterJob, setEnterJob] = useState(false);
const [jobSelection, setJobSelection] = useState(Array(someList.length).fill(false));
const jobRef = useRef();
const handleJobClick = i => {
const n = parseInt(i.target.id.charAt(0)); // the list is small enough to allow this
let c = jobSelection;
c[n] = !c[n];
setJobSelection(c);
};
const handleMouse = (e) =>{
if (!jobRef.current.contains(e.target)){
setEnterJob(false);
};
};
useEffect(() => {
window.addEventListener("mousedown", handleMouse);
return () => window.removeEventListener("mousedown", handleMouse);
});
return(
<div ref={jobRef}>
<input onFocus={()=> setEnterJob(true)} />
<div style={{display: `${enterJob ? 'flex' : 'none'}`}} >
<ul>
{ someList.map((item,index)=>
<li id={`${index}`} onClick={handleJobClick}> {jobSelection[index] ? item : "you clicked on the button"} </li> )}
</ul>
</div>
</div>
)
}
一些解释:我正在使用 UseEffect 和 useRef 创建一个下拉菜单,当您在容器外单击时该菜单会消失。现在,当我想点击此下拉菜单的值时,它不会更新 DOM,而我正在使用 useState 来更新负责更改的字符串的值。
提前谢谢你, 夏贝尔
问题是您正在改变 jobSelection
而不是创建新对象。如果对象具有与以前相同的引用,则 React 将跳过重新渲染:
const handleJobClick = i => {
const n = parseInt(i.target.id.charAt(0)); // the list is small enough to allow this
let c = [...jobSelection]; // Create a new array
c[n] = !c[n];
setJobSelection(c);
};
问题
如果我理解你的问题,那么我相信这是因为你直接改变了你的状态。
const handleJobClick = i => {
const n = parseInt(i.target.id.charAt(0)); // the list is small enough to allow this
let c = jobSelection;
c[n] = !c[n]; // <-- mutation!
setJobSelection(c);
};
您还缺少映射列表项上的反应键。
解决方案
由于下一个状态取决于前一个状态,您应该先使用功能状态更新来复制您的状态,然后更新它。
我建议:
- 将
handleJobClick
转换为直接使用索引,柯里化函数可以干净地处理这个问题 - 向映射的列表项添加反应键
代码
const handleJobClick = index => () => {
setJobSelection(jobSelection => jobSelection.map(
(selection, i) => index === i ? !selection : selection // <-- toggle selection at matched index
);
};
...
<ul>
{someList.map((item, index)=> (
<li
key={index} // <-- index as react key, ok since not adding/removing/sorting jobs
onClick={handleJobClick(index)} // <-- pass index to handler
>
{jobSelection[index] ? item : "you clicked on the button"}
</li>
))}
</ul>