useState 中的状态不会改变

时间:2021-07-28 10:51:00

标签: javascript reactjs react-hooks

我有一个功能组件

import React, { useState } from 'react'

export default function AboutUs() {
const [counter,setCounter] = useState(0);

const clicked = () => {
    setCounter(counter+1);
}

return (
    <div>
        <p>you clicked {counter} times</p>
        <button onClick={clicked}>Click</button>        
    </div>
)
}

这没有任何问题,而且效果很好。但是当我删除按钮元素中的 onClick 并添加事件侦听器时:

import React, { useEffect, useState } from 'react'

export default function AboutUs() {
const [counter,setCounter] = useState(0);

useEffect(()=>{
    document.getElementById('test').addEventListener('click',clicked);
},[])

const clicked = () => {
    setCounter(counter+1);
}

return (
    <div>
        <p>you clicked {counter} times</p>
        <button id="test">Click</button>        
    </div>
)
}

点击一次后计数器值将是1然后停止! counter 不再被 setCounter 改变! 这有什么问题? 感谢您的帮助。

2 个答案:

答案 0 :(得分:7)

<块引用>

我有一个功能组件

你没有。

您的组件跟踪违反函数式编程原则的状态,即函数应该没有副作用

您有一个功能组件


<块引用>

计数器不再被 setCounter 改变!

事件处理程序附加在 useEffect 钩子中。

当组件第一次挂载以及任何依赖项发生变化时,就会调用该钩子。

依赖项列表是 [] 所以依赖项永远不会改变。

每次调用(我们刚刚建立的只有一次)它会创建一个函数,closes over counter 常量(最初设置为 0

每次点击元素时,您都会得到 setCounter(counter+1);,其中 counter 始终为 0,因为这是关闭的常量的值。

(即使更改状态会触发重新渲染,从而创建具有相同名称的不同常量)。


要使用该方法执行此操作,您通常会执行以下操作:

useEffect(()=>{
    document.getElementById('test').addEventListener('click',clicked);
    return () => document.getElementById('test').removeEventListener('click', 'clicked');
},[clicked])

因此,每次函数更改时,它都会清除先前的事件侦听器并添加新的事件侦听器(已关闭新常量)


但是直接 DOM 操作是脆弱的,并且会抛弃 React 的主要优势。所以不要那样做。使用 onClick={click} 方法,这就是 React 的工作原理。

答案 1 :(得分:1)

这里有两个问题:

  1. useEffect 依赖项未指定
  2. useEffect 没有清理处理程序 更新您的代码会得到如下结果:
useEffect(() => {
  const element = document.getElementById('test');
  element.addEventListener('click', clicked);
  return () => { element.removeEventListener('click', clicked) };
}, [clicked]);

但仍然存在一个问题:clicked 函数引用在每次渲染时都会发生变化,这意味着 useEffect 将在每次渲染时移除/添加侦听器。 为避免这种情况,您应该将点击的处理程序包装在具有适当依赖项的 useCallback 中,如下所示:

const clicked = useCallback(() => {
    setCounter(counter + 1);
}, [counter]);
相关问题