mouseEnter 和 mouseLeave 在 HTML 元素上工作,而不是 React 组件?

mouseEnter and mouseLeave work on HTML elements, but not React components?

第一次制作 React 网站(使用 Gatsby)。

想要发生的事情

在我的索引页面上,我试图在鼠标悬停在 Term 组件上时显示 Note 组件。

正在发生什么

当我将 onMouseEnteronMouseLeave 直接添加到 Term 组件时,Note 永远不会出现。但是,如果我使用本机 html 元素(下面示例代码中的 span)而不是 <Term/>,则会出现 Note

和箭头函数有关系吗?

这与我如何编写组件有关吗?还是 useState 挂钩的限制? 我在这里做的事情看起来相当 simple/straightforward,但是我还是新手,所以可能有一些我不知道的规则。

示例代码

索引页

//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;

useEffectuseRefforwardRefaddEventListener/removeEventListener 的解决方案:

App.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>
  );
}

如果要添加其他术语,只需创建一个新的 Term 组件并添加 refwordsymbol(可选)道具。

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;

Note.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>
  );
}

演示: Stackblitz

当您在 JSX 中使用组件时,与(小写)HTML 标签相反,任何属性仅作为 prop 传递,不会直接产生进一步的影响。

<Term
  word="TermB" symbol="†"
  onMouseEnter={() => setShowNoteB(true)}
  onMouseLeave={() => setShowNoteB(false)}
/>

您需要获取传递的属性并将其分配给组件 JSX 中的实际 HTML 元素,如下所示:

const Term = ({ word, symbol, onMouseEnter, onMouseLeave }) => {
  return (
    <div onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>
      <span>{word}</span> <span>{symbol}</span>
    </div>
  );
};