es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

useCallback devuelve n+1 veces donde n es el número de variables de estado.

Estoy tratando de configurar un menú contextual personalizado, sin embargo, cada vez que el usuario hace clic derecho, la función del menú contextual devuelve 6 veces diferentes, siendo la quinta lo que necesito y la sexta los valores predeterminados del estado. Sin embargo, si el usuario hace doble clic derecho en el mismo lugar, devuelve 5 veces, con la quinta devolución siendo los valores deseados y se abre el menú. ¿Existe alguna manera de verificar antes de la devolución si todos los estados han cambiado y devolver solo desde la devolución si toda la información necesaria está presente?

const ContextMenu = outerRef => {
const [xPos, setXPos] = useState(“0px”);
const [yPos, setYPos] = useState(“0px”);
const [menu, showMenu] = useState(false);
const [menuTarget, setMenuTarget] = useState(”);
const [menuTargetId, setMenuTargetId] = useState(”);
const handleContextMenu = useCallback(
event => {
if(event.target.className && (event.target.className.includes(‘bar’) || event.target.className == ‘timeline’ || event.target.className == ‘draggablediv’ || event.target.className == ‘editableDiv’)){
event.preventDefault();
if (outerRef && outerRef.current.contains(event.target)) {
setXPos(${event.pageX}px);
setYPos(${event.pageY}px);
setMenuTarget(event.target.className)
setMenuTargetId(event.target.id)
showMenu(true);
} else {
showMenu(false);
}
}
},[showMenu, outerRef, setXPos, setYPos]);

const handleClick = useCallback(() => {
showMenu(false);
}, [showMenu]);

useEffect(() => {
document.addEventListener(“click”, handleClick);
document.addEventListener(“contextmenu”, handleContextMenu);
return () => {
document.removeEventListener(“click”, handleClick);
document.removeEventListener(“contextmenu”, handleContextMenu);
};
}, []);

return {xPos, yPos, menu, menuTarget, menuTargetId};
};

Tags:  , ,

Answer

  1. Avatar for davy.ai

    Sí, puedes comprobar antes del retorno si todos los estados han cambiado y solo retornar desde el callback si toda la información necesaria está presente. Puedes usar el gancho useEffect para agregar un escucha de eventos para el evento contextmenu y comprobar si toda la información necesaria está presente antes de retornar desde el callback. El fragmento de código a continuación muestra un ejemplo de cómo se puede hacer esto:

    const ContextMenu = outerRef => {
      const [xPos, setXPos] = useState("0px");
      const [yPos, setYPos] = useState("0px");
      const [menu, showMenu] = useState(false);
      const [menuTarget, setMenuTarget] = useState('');
      const [menuTargetId, setMenuTargetId] = useState('');
      const handleContextMenu = useCallback(
        event => {
          if(event.target.className && (event.target.className.includes('bar') ||event.target.className == 'timeline' || event.target.className == 'draggablediv' || event.target.className == 'editableDiv')){
             event.preventDefault();
             if (outerRef && outerRef.current.contains(event.target)) {
                setXPos(`${event.pageX}px`);
                setYPos(`${event.pageY}px`);
                setMenuTarget(event.target.className)
                setMenuTargetId(event.target.id)
                showMenu(true);
             } else {
             showMenu(false);
           }
         }
      },[showMenu, outerRef, setXPos, setYPos]);
    
      const handleClick = useCallback(() => {
        showMenu(false);
      }, [showMenu]);
    
      useEffect(() => {
        document.addEventListener("click", handleClick);
        document.addEventListener("contextmenu", handleContextMenu);
        return () => {
          document.removeEventListener("click", handleClick);
          document.removeEventListener("contextmenu", handleContextMenu);
        };
      }, []);
    
      return {xPos, yPos, menu, menuTarget, menuTargetId};
    };
    

Comments are closed.