Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/410.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 当从prop事件处理程序设置时,反应状态重置_Javascript_Reactjs_React Hooks - Fatal编程技术网

Javascript 当从prop事件处理程序设置时,反应状态重置

Javascript 当从prop事件处理程序设置时,反应状态重置,javascript,reactjs,react-hooks,Javascript,Reactjs,React Hooks,我一辈子都搞不清楚到底发生了什么,但由于某种原因,当点击“Click me”时,数字会像你所期望的那样增加。当子组件触发单击时,它将重置状态并始终打印0 function Child(props: { onClick?: (id: string) => void, }) { const ref = useCallback((ref) => { ref.innerHTML = 'This Doesnt'; ref.addEventList

我一辈子都搞不清楚到底发生了什么,但由于某种原因,当点击“Click me”时,数字会像你所期望的那样增加。当子组件触发单击时,它将重置状态并始终打印0

function Child(props: {
    onClick?: (id: string) => void,
}) {
    const ref = useCallback((ref) => {
        ref.innerHTML = 'This Doesnt';
        ref.addEventListener('click',() => {
            props.onClick!('')
        })
    }, [])
    return (<div ref={ref}></div>)
}

function Parent() {
  const [number, setNumber] = useState(0);
  return <div>
            <div onClick={() => {
                setNumber(number + 1);
                console.log(number);
            }}>
                This Works
            </div>
            <Child
                onClick={(id) => {
                    setNumber(number + 1);
                    console.log(number);;
                }}
            />
        </div>
}
函数子级(道具:{
onClick?:(id:string)=>void,
}) {
常量ref=useCallback((ref)=>{
ref.innerHTML='This Doesnt';
ref.addEventListener('单击',()=>{
道具。onClick!(“”)
})
}, [])
返回()
}
函数父函数(){
const[number,setNumber]=useState(0);
返回
{
设置编号(编号+1);
控制台日志(编号);
}}>
这很有效
{
设置编号(编号+1);
控制台日志(编号);;
}}
/>
}

下面是问题的演示:

父组件中的
onClick
处理程序在每次渲染时都会重新创建,因为它们在
number
状态字段上有一个闭包

问题是,发送到子组件的
onClick
属性在
ref
回调中使用,由于依赖项列表为空,只有在初始渲染时才会对其作出反应。因此,子级在后续渲染中接收的
onClick
prop根本不会被使用

若要解决此错误,请删除dependency param或发送props.onClick,就像在dependency list中一样,由于文档中提到的警告,我们遇到了问题

因此,您添加了null处理,现在可以调用更新的回调,但是。。。所有早期的回调都会被调用,因为我们没有删除这些事件侦听器

const ref = useCallback((ref) => {
    if(!ref) return;
    ref.innerHTML = 'This Doesnt';
    ref.addEventListener('click',() => {
        props.onClick!('')
    })
}, [props.onClick])
我相信这只是作为学习挂钩的一部分进行的一个实验,否则就不需要迂回地从ref回调调用
onClick
。只需将其作为道具传递给
div

编辑:
根据您的评论,由于这不仅仅是一个实验,而是一些真实需求的简化,需要通过
addEventListener
设置点击处理程序,因此这里有一个可能的解决方案:

const ref = useRef(null);
useEffect(() => {
    if(!ref.current) return;
    ref.current.innerHTML = 'This Doesnt';
    const onClick = () => props.onClick!('');
    ref.current.addEventListener('click',onClick)

    // return the cleanup function to remove the click handler, which will be called before this effect is run next time.
    return () => {ref.current.removeEventListener("click", onClick)}
}, [ref.current, props.onClick]);
基本上,我们需要使用
useffect
,以便在添加新的侦听器之前有机会删除旧的侦听器

希望这有帮助。

函数子(道具:{
function Child(props: {
    onClick?: (id: string) => void,
}) {
    function handleClick() {
      props.onClick('')
    }
    const ref = useRef();
    useEffect(() => {
      ref.current.innerHTML = 'This Doesnt';
      ref.current.addEventListener('click', handleClick)
      return () => { ref.current.removeEventListener('click', handleClick); }
    }, [props.onClick])

    return (<div ref={ref}></div>)
}
onClick?:(id:string)=>void, }) { 函数handleClick(){ 道具。onClick(“”) } const ref=useRef(); useffect(()=>{ ref.current.innerHTML='This Doesnt'; ref.current.addEventListener('click',handleClick) return()=>{ref.current.removeEventListener('click',handleClick);} },[props.onClick]) 返回() }
几乎可以工作,但console.log显示了从0到当前数值的所有数字。
问题是在子组件umount之后未删除的事件侦听器,因此为了实现这一点,我在卸载侦听器时使用了useffect和return函数。

在SVGView中使用useCallback()的原因是什么?我想知道这是否是你的问题的原因?获取div的引用。在这种情况下,可能是更好的fitUse ref也有同样的问题..是bang on
props.onClick!(“”)
打字脚本的特殊功能?另外,为什么要将
onClick
包装在闭包中,而不是直接将其作为回调参数传递给
addEventListener
?在我的简化过程中,所有这些的推理都丢失了。我有一个SVG视图类,它对SVG进行大量操作,因此使用snapsvg。我将点击事件添加到各种svg元素中,并一直试图避免每次道具更改时解析和重新渲染整个svg,但看起来如果我要使用挂钩,我的最佳选择是在每次渲染后重新创建onclick处理程序。在这种情况下,可能会恢复使用类。我已经用可能的解决方案更新了答案。请检查一下。刚刚注意到@kwdowik提供了类似的解决方案。谢谢。这就是我最终要做的。实际上,这一切都比仅仅使用一个类更混乱,但至少它是有趣的!再次感谢。