我有一个功能组件
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 in按钮元素并添加事件侦听器时,如:
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>
)
}
一键后计数器Value将为1,然后停止!柜台setCounter了!这有什么不对吗?我很感激你的帮助。
我有一个功能组件
你没有。
组件跟踪状态违反了函数式编程的原则,即函数应该具有无副作用.
你有一个函数组件.
计数器不再被setCounter改变!
事件处理程序附加在useEffect
钩子中。
当组件第一次挂载或任何依赖项发生变化时,该钩子被调用。
依赖项列表是[]
,所以依赖项永远不会改变。
每次调用它(我们刚刚建立的只有一次),它将创建一个函数,关闭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的设计工作方式。
这里有两个问题:
useEffect
依赖项未指定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]);