Javascript 为什么一个函数通过道具传递到一个子组件中,在按键和点击按钮时表现不同?

Javascript 为什么一个函数通过道具传递到一个子组件中,在按键和点击按钮时表现不同?,javascript,reactjs,react-hooks,Javascript,Reactjs,React Hooks,我有一个React项目,其中父组件(功能)保持状态,以确定应用于列表的格式。当用户单击按钮时,会生成一个模式-我将匿名函数作为道具传递给该模式(onMainButtonClick),调用该函数时会翻转状态并更改列表的格式(此逻辑在父组件中) 当我在模态组件中使用按钮(onClick={()=>onMainButtonClick()})时,代码按预期工作。但是,我也希望按下enter键来触发此操作。因此,我为此实现了以下代码,但它没有按预期工作。模式关闭,函数启动(我知道这是因为我在那里放了一个控

我有一个React项目,其中父组件(功能)保持状态,以确定应用于列表的格式。当用户单击按钮时,会生成一个模式-我将匿名函数作为道具传递给该模式(
onMainButtonClick
),调用该函数时会翻转状态并更改列表的格式(此逻辑在父组件中)

当我在模态组件中使用按钮(
onClick={()=>onMainButtonClick()}
)时,代码按预期工作。但是,我也希望按下enter键来触发此操作。因此,我为此实现了以下代码,但它没有按预期工作。模式关闭,函数启动(我知道这是因为我在那里放了一个控制台日志…),但是影响格式的状态没有改变

编辑:在下面做了一个建议的更改(对onEnterPress函数进行备注,以便正确删除它),下面是模式的完整代码:

import { React, useRef, useEffect, useCallback } from "react";
import ReactDOM from "react-dom";

const Modal = ({
  title,
  description,
  isOpen,
  onClose,
  onMainButtonClick,
  mainButtonText,
  secondaryButtonText,
  closeOnly,
}) => {
  const node = useRef();

  const onEnterPress = useCallback(
    (e) => {
      if (e.key === "Enter") {
        onMainButtonClick();
      }
    },
    [onMainButtonClick]
  );

  useEffect(() => {
    window.addEventListener("keydown", onEnterPress);
    return () => {
      window.removeEventListener("keydown", onEnterPress);
    };
  }, [onMainButtonClick]);

  const handleClickOutside = (e) => {
    if (node.current.contains(e.target)) {
      return null;
    }
    onClose();
  };

  if (!isOpen) return null;
  return ReactDOM.createPortal(
{// JSX here, removed for brevity},
    document.body
  );
};

export default Modal;

以及父项的代码(显示模式并向下传递到MainButtonClick):

从“React”导入{React,useState};
从“html实体”导入{decode};
从“../../queries/useList”导入useList;
从“../../translations/useBuyItem”导入useBuyItem;
从“./共享/标题”导入标题;
从“./Shared/Description”导入说明;
从“/ListItem”导入ListItem;
从“/BuyOverlay”导入BuyOverlay;
从“./Modal”导入模态;
从“/ViewOperations”导入ViewOperations;
常量视图=(道具)=>{
const[buyOverlayOpen,setBuyOverlayOpen]=useState(false);
const[ViewBuyerOverlayopen,SetBuyerOverlayopen]=useState(false);
const[viewBullers,setviewBullers]=useState(false);
const[buyItemId,setBuyItemId]=useState();
const{isLoading,isError,data,error}=useList(props.match.params.id);
const buyItemMutation=useBuyItem(buyItemId,props.match.params.id,()=>
setBuyOverlayOpen(假)
);
如果(孤岛加载){
返回加载列表。。。;
}
如果(isError){
返回发生错误,请刷新后重试;
}
返回(
SetViewBullers(!ViewBullers)}
ToggleBuyerOverlay={()=>
SetBuyerOverlayopen(!ViewBuyerOverlayopen)
}
viewbullers={viewbullers}
/>
{data.items.length==0
?“此列表中未添加任何项目”
: ""}
    {data.items.map((item)=>{ 返回( { setBuyItemId(项目编号); setBuyOverlayOpen(真); }} key={item.\u id} viewbullers={viewbullers} /> ); })}
setBuyOverlayOpen(假)} OnConfigrmClick={(buyerName)=>{ 突变(buyerName); }} /> { SetBuyerOverlayopen(假); }} onMainButtonClick={()=>{ SetViewBudders(真); SetBuyerOverlayopen(假); }} mainButtonText=“确定” secondaryButtonText=“取消” /> ); }; 导出默认视图;

你知道为什么会这样吗?我觉得这可能与这里的useEffect有关,但我有点不知所措……

onenterprisess
很可能直接定义在功能组件中,这就是为什么每次组件重新渲染时它的引用都会更改。您的
useffect
会在每次渲染时关闭此新定义的函数,因此处理程序将无法按预期工作。 您可以使用
useCallback
包装您的
onEnterPress
,以记忆处理程序
onEnterPress
,使其定义在整个渲染过程中保持不变,如下所示:

const onEnterPress = useCallback(e => {
    if (e.key === "Enter") {
      onMainButtonClick();
    }
  }, [onMainButtonClick]); // Another function dep. You can also wrap it with useCallback or carry over its logic into the callback here
  useEffect(() => {
    window.addEventListener('keydown', onEnterPress);
    return () => {
      window.removeEventListener('keydown', onEnterPress);
    };
  }, [onEnterPress]);

我最终发现了这一点-结果很简单,因为我的enterPress回调需要在if块之前使用preventDefault语句(
e.preventDefault()


页面上一定有其他东西捕获了enter键并导致其行为异常。

首先,您对效果有一个未声明的依赖项,即
onMainButtonClick
,这可能是问题的一部分,但这取决于您未提供的周围代码。你能扩展这个问题并提供更多的细节吗?那么你想在页面上听回车吗?是的,当模态打开时,我想在页面上听回车@PierPaoloRamon我将尝试在上面给出更多的细节,这是有意义的,而且听起来是一个很好的调整,因为正如您所描述的,onenterprisess是在功能组件内部定义的。然而,我已经做出了改变,仍然遇到同样的问题。将尝试在上面发布更多细节。
const onEnterPress = useCallback(e => {
    if (e.key === "Enter") {
      onMainButtonClick();
    }
  }, [onMainButtonClick]); // Another function dep. You can also wrap it with useCallback or carry over its logic into the callback here
  useEffect(() => {
    window.addEventListener('keydown', onEnterPress);
    return () => {
      window.removeEventListener('keydown', onEnterPress);
    };
  }, [onEnterPress]);