React useState 由于 useRef 而没有更新

Posted

技术标签:

【中文标题】React useState 由于 useRef 而没有更新【英文标题】:React useState not updating because of useRef 【发布时间】:2021-03-01 04:42:11 【问题描述】:

我的反应代码遇到了一个非常奇怪的问题: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 更新负责更改的字符串的值。

提前谢谢你, 查贝尔

【问题讨论】:

【参考方案1】:

问题是你正在改变你的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);
    ;

【讨论】:

太好了,非常感谢!我需要了解更多关于突变的知识。【参考方案2】:

问题

如果我理解你的问题,那么我相信这是因为你直接改变了你的状态。

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>

【讨论】:

完美!非常感谢就是这样。 @Domino987 的解决方案有点简单,但这个更容易理解。 没问题,对象、数组甚至字符串和布尔值也是如此。如果您有任何问题,请联系我 @CharbelImad 当然可以。我注意到其他几个问题,因此我更新了答案以帮助解决这些问题。 感谢您的更正。以这种方式传递索引值时我遇到了一个奇怪的问题@drew-reese:我得到一个无限循环渲染错误。我会更仔细地查看并回复您。 @CharbelImad 您是否更改了handleJobClick 函数签名? handleJobClick = index =&gt; () =&gt; ...?这是一个柯里化函数,它获取一个索引并返回一个用作回调的函数,即它接受事件但由于我们不关心事件对象中的 anything 我们不需要分配它是一个标识符。

以上是关于React useState 由于 useRef 而没有更新的主要内容,如果未能解决你的问题,请参考以下文章

反应:useState 还是 useRef?

重载与 useRef 不匹配

react hooks 之 useRef, useImperativeHandle

react hooks 之 useRef, useImperativeHandle

在 React-Hooks UseRef 我无法获得新的状态值

这里的 useRef 内部发生了啥?