Primera vez haciendo un sitio React (usando Gatsby).
En mi página de índice, estoy tratando de hacer que aparezca un componente Note cuando el mouse pasa sobre un componente Term .
Cuando agrego onMouseEnter y onMouseLeave directamente al componente Term , la Note nunca aparece. Sin embargo, si uso un elemento html nativo ( span en el código de ejemplo a continuación) en lugar de <Term/> , aparece la Note .
¿Tiene algo que ver con la función de flecha?
() => setShowNoteB(true) con console.log("in TermB") (más o menos) funciona.onMouseEnter={setShowNoteB(true)} da como resultado un error de bucle infinito. ¿Está relacionado con cómo estoy componiendo los componentes? ¿O es una limitación del gancho useState ? Lo que estoy haciendo aquí parece bastante simple/directo, pero nuevamente, soy nuevo en esto, así que tal vez haya alguna regla que desconozco.
//index.js import * as React from 'react'; import { useState } from 'react'; import Term from '../components/term'; import Note from '../components/note'; const IndexPage = () => { const [showNoteA, setShowNoteA] = useState(false); const [showNoteB, setShowNoteB] = useState(false); return ( <> <h1 > <span onMouseEnter={() => setShowNoteA(true)} onMouseLeave={() => setShowNoteA(false)} > TermA* </span> {" "} doesn't behave like {" "} <Term word="TermB" symbol="†" onMouseEnter={() => setShowNoteB(true)} onMouseLeave={() => setShowNoteB(false)} /> </h1> {showNoteA ? <Note> <p>This is a note for TermA.</p> </Note> : null} {showNoteB ? <Note> <p>This is a note for TermB.</p> </Note> : null} </> ); }; export default IndexPage // term.js import React from 'react'; const Term = ({ word, symbol }) => { return ( <div> <span>{word}</span> <span>{symbol}</span> </div> ); }; export default Term; // note.js import * as React from 'react'; const Note = ({ children })=> { return ( <div> {children} </div> ) }; export default Note;Solución con useEffect , useRef , forwardRef y addEventListener/removeEventListener :
Aplicación.js
import React, { useRef } from 'react'; import Term from './Term'; export default function App() { const ref = useRef({}); return ( <div> Lorem ipsum dolor sit amet,{' '} <Term ref={ref} word="consectetur" symbol="†" /> adipiscing elit. Morbi laoreet <Term ref={ref} word="lacus" /> in dui vestibulum, nec imperdiet augue vulputate. </div> ); } Si desea agregar otros términos, simplemente cree un nuevo componente Término y agregue los accesorios ref , word , symbol (opcional).
Term.js
import React, { useState, useEffect, forwardRef } from 'react'; import Note from './Note'; function Term({ word, symbol }, ref) { const [isHovered, setHovered] = useState(false); const [currentTerm, setCurrentTerm] = useState(''); const [currentSymbol, setCurrentSymbol] = useState(''); useEffect(() => { if (ref.current && word) { ref.current[word].addEventListener('mouseover', handleMouseover); ref.current[word].addEventListener('mouseout', handleMouseout); return () => { ref.current[word].removeEventListener('mouseover', handleMouseover); ref.current[word].removeEventListener('mouseout', handleMouseout); }; } }, [ref.current, word]); const handleMouseover = () => { setHovered(true); setCurrentTerm(word); setCurrentSymbol(symbol); }; const handleMouseout = () => { setHovered(false); setCurrentTerm(''); setCurrentSymbol(''); }; return ( <> <span ref={(elem) => (ref.current[word] = elem)}> {word} </span> {isHovered ? <Note word={currentTerm} symbol={currentSymbol} /> : null} </> ); } const forwarded = forwardRef(Term); export default forwarded;nota.js
import React from 'react'; export default function Note({ word, symbol }) { const checkTerm = (term) => { switch (term) { case 'consectetur': return `definition`; case 'lacus': return `definition`; default: return null; } }; return ( <div> <p> {word} {symbol ? symbol : '-'} </p> <p>{checkTerm(word)}</p> </div> ); }Demostración: Stackblitz
Cuando usa Componentes en su JSX, a diferencia de las etiquetas HTML (en minúsculas), cualquier atributo solo se pasa como accesorio y no tiene ningún efecto directo.
<Term word="TermB" symbol="†" onMouseEnter={() => setShowNoteB(true)} onMouseLeave={() => setShowNoteB(false)} />Debe tomar el apoyo pasado y asignarlo a un elemento HTML real en el JSX del componente, así:
const Term = ({ word, symbol, onMouseEnter, onMouseLeave }) => { return ( <div onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}> <span>{word}</span> <span>{symbol}</span> </div> ); };